{"record":{"id":"b1e5e743d5b35389","repo":"santifer/career-ops","slug":"local-parser-the-parser-script-must-be-the-interp","errorCode":null,"errorMessage":"local-parser: the parser script must be the interpreter's first argument","messagePattern":"local-parser: the parser script must be the interpreter's first argument","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/local-parser.mjs","lineNumber":114,"sourceCode":"  return resolveInsideRoot(value);\n}\n\n// Validate the whole invocation and return what to spawn. Throws on anything unsafe.\nfunction resolveInvocation(entry) {\n  const rawCommand = String(entry.parser?.command || '');\n  const command = resolveCommand(rawCommand);\n  const args = buildParserArgs(entry);\n  const scriptPath = getParserScriptPath(entry);\n\n  const usesInterpreter = !rawCommand.includes('/') && ALLOWED_INTERPRETERS.has(rawCommand);\n  if (usesInterpreter) {\n    // A whitelisted interpreter must run an in-repo script as its FIRST argument.\n    // Anything before the script is an interpreter option (node --eval / --require,\n    // python -c, …) that could execute arbitrary code, so require the script to lead.\n    if (!scriptPath) throw new Error('local-parser: interpreter command requires an in-repo parser script');\n    resolveInsideRoot(scriptPath);\n    if (args[0] !== scriptPath) {\n      throw new Error('local-parser: the parser script must be the interpreter\\'s first argument');\n    }\n  } else if (scriptPath) {\n    // command is an in-repo file; keep any detected script path inside the repo too.\n    resolveInsideRoot(scriptPath);\n  }\n\n  return { command, args };\n}\n\nfunction normalizeJobUrl(rawUrl, baseUrl) {\n  if (!rawUrl) return '';\n  try {\n    return new URL(String(rawUrl).trim(), baseUrl || undefined).href;\n  } catch {\n    return '';\n  }\n}\n","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/providers/local-parser.mjs#L96-L132","documentation":"For a whitelisted interpreter command, the in-repo parser script must be args[0]. Anything before the script could be an interpreter option (node --require, python -c) that executes arbitrary code, so resolveInvocation() enforces the script leads the argument list.","triggerScenarios":"Entry where a script is detected (via parser.script or a *.py/.mjs/.js/.sh arg) but the expanded args array does not start with it — e.g. {command: python3, script: parsers/jobs.py, args: [\"-u\", \"parsers/jobs.py\"]} puts '-u' first.","commonSituations":"Adding interpreter flags (-u, --harmony, -O) before the script path; parser.script set while parser.args also repeats the script after some flag; placeholder expansion reordering assumptions; copying a shell invocation verbatim into args.","solutions":["Reorder parser.args so the script path is the first element; pass interpreter flags via the command's supported config or drop them.","If parser.script is set and args also includes the script, remove the duplicate and keep args[0] equal to the script path string.","Verify after {careers_url}/{company} expansion that args[0] still string-equals the script path (expansion can change the string).","Use the direct in-repo-file form instead (command: parsers/jobs.py style) if you don't need interpreter options — no shebang/exec bit needed only for interpreters, so prefer keeping the interpreter with the script first."],"exampleFix":"// before (portals.yml)\nparser: {command: python3, script: parsers/jobs.py, args: [\"-u\", \"parsers/jobs.py\"]}\n// after\nparser: {command: python3, script: parsers/jobs.py, args: [\"parsers/jobs.py\", \"-u\"]}","handlingStrategy":"validation","validationCode":"const scriptPath = String(entry.parser?.script || (entry.parser?.args || []).find(a => /\\.(py|mjs|js|sh)$/.test(String(a))) || '');\nconst first = String((entry.parser?.args || [])[0] || '');\nif (scriptPath && first !== scriptPath) throw new Error(`${entry.name}: script must be parser.args[0]`);","typeGuard":null,"tryCatchPattern":"try {\n  await localParser.fetch(entry);\n} catch (e) {\n  if (String(e.message).includes(\"must be the interpreter's first argument\")) {\n    console.error(`${entry.name}: reorder parser.args so the script path comes first`);\n    return [];\n  }\n  throw e;\n}","preventionTips":["Put the script path as the first element of parser.args whenever parser.script is set.","Avoid duplicating the script in both script: and args:.","Re-verify arg order after placeholder expansion.","Skip interpreter flags (-u, etc.) unless the script still leads args."],"tags":["security","config","argument-order"],"backgroundTag":"invalid-config-value","analyzedSha":"aac998c7ed7248ea853b720ceeb1fdbeb322fc5d","analyzedAt":"2026-09-16T06:35:29.214Z","contentChangedAt":"2026-09-16T06:35:29.214Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}