{"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":"exception","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/9b17a8ac97b398a496b38e423ae24e433b43254f/providers/local-parser.mjs#L96-L132","documentation":"For a whitelisted interpreter, the parser script must be args[0] — the interpreter's first argument. If anything precedes the script (an interpreter option like --eval, --require, -c, --inspect), this error fires. The rule blocks interpreter options that could execute arbitrary code (node --eval '...', python -c '...').","triggerScenarios":"parser.args lists an option before the script, e.g. args: ['--require','./hook.js','parsers/acme.py'], or the script is not the first element. args[0] !== scriptPath triggers the throw.","commonSituations":"A developer tried to inject a --require hook or environment setup before the script; args ordering was changed; the script was placed later in the array by mistake.","solutions":["Reorder parser.args so the script path is first: args: ['parsers/acme.py', '--require','./hook.js'] (note: options after the script are passed to the script, not the interpreter — adjust accordingly).","If you need interpreter options, do not use local-parser's argv expansion; wrap the invocation in an in-repo shell script instead.","Remove any leading --eval/-c style options."],"exampleFix":"# before\nparser:\n  command: node\n  script: parsers/acme.js\n  args: ['--require', './hook.js', 'parsers/acme.js']\n\n# after (hook logic moved inside the parser script)\nparser:\n  command: node\n  script: parsers/acme.js\n  args: ['parsers/acme.js', '{careers_url}']","handlingStrategy":"validation","validationCode":"const INTERPRETERS = new Set(['python3','python','node','deno','bun','sh','bash']);\nexport function scriptIsFirstArg(entry) {\n  const cmd = String(entry?.parser?.command || '');\n  if (!INTERPRETERS.has(cmd)) return true;\n  const script = entry?.parser?.script;\n  const args = Array.isArray(entry?.parser?.args) ? entry.parser.args : [];\n  return !script || args[0] === script;\n}","typeGuard":"const INTERPRETERS = new Set(['python3','python','node','deno','bun','sh','bash']);\n/** @param {any} entry */\nfunction argsLeadWithScript(entry) {\n  const cmd = String(entry?.parser?.command || '');\n  if (!INTERPRETERS.has(cmd)) return true;\n  const script = entry?.parser?.script;\n  const args = Array.isArray(entry?.parser?.args) ? entry.parser.args : [];\n  return !script || args[0] === script;\n}","tryCatchPattern":"try {\n  await provider.fetch(entry, ctx);\n} catch (err) {\n  if (err.message.includes(\"first argument\")) console.warn(`reorder parser.args so the script leads for ${entry.name}`);\n  throw err;\n}","preventionTips":["Always place the script path as the first element of parser.args when using an interpreter command.","Move any interpreter setup (--require, hooks) into the script itself rather than the argv.","Lint parser.args to ensure no leading options precede the script for interpreter commands.","Treat this error as a code-execution-guard trip — do not silence it by reordering blindly."],"tags":["argument-injection","code-execution-guard","security","local-parser","interpreter"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}