{"record":{"id":"e82fe61b1ae6b6e3","repo":"santifer/career-ops","slug":"local-parser-interpreter-command-requires-an-in-r","errorCode":null,"errorMessage":"local-parser: interpreter command requires an in-repo parser script","messagePattern":"local-parser: interpreter command requires an in-repo parser script","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/local-parser.mjs","lineNumber":111,"sourceCode":"  const value = String(command || '');\n  if (!value) throw new Error('local-parser: parser.command is required');\n  if (!value.includes('/') && ALLOWED_INTERPRETERS.has(value)) return value;\n  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 '';","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/providers/local-parser.mjs#L93-L129","documentation":"When parser.command is a whitelisted interpreter (python3/python/node/deno/bun/sh/bash — no '/' and in ALLOWED_INTERPRETERS), the provider requires a corresponding in-repo parser script path. If getParserScriptPath returns nothing (no parser.script / detectable script arg), this error fires. The rule prevents running an interpreter with no controlled script, which could otherwise let interpreter options execute arbitrary code.","triggerScenarios":"entry.parser.command is 'python3' (or another whitelisted interpreter) but parser.script is unset and no arg looks like a script path. usesInterpreter is true, scriptPath is falsy.","commonSituations":"An entry sets command: node but forgets script:; the script path was put in a different key; the parser args were intended to be inline code (not allowed — see error 254).","solutions":["Add parser.script pointing at an in-repo script, e.g. script: parsers/acme.py.","Ensure the script path is under the project root (it will be checked by resolveInsideRoot next).","If you intended a self-contained command without a script, local-parser is the wrong provider — write a script file."],"exampleFix":"# before\nparser: { command: python3, args: ['{careers_url}'] }\n\n# after\nparser: { command: python3, script: parsers/acme.py, args: ['{careers_url}'] }","handlingStrategy":"validation","validationCode":"const INTERPRETERS = new Set(['python3','python','node','deno','bun','sh','bash']);\nexport function interpreterHasScript(entry) {\n  const cmd = String(entry?.parser?.command || '');\n  if (!cmd || cmd.includes('/') || !INTERPRETERS.has(cmd)) return true; // not an interpreter command\n  return Boolean(entry?.parser?.script);\n}","typeGuard":"const INTERPRETERS = new Set(['python3','python','node','deno','bun','sh','bash']);\n/** @param {any} entry */\nfunction isValidInterpreterEntry(entry) {\n  const cmd = String(entry?.parser?.command || '');\n  if (!INTERPRETERS.has(cmd)) return true;\n  return typeof entry?.parser?.script === 'string' && entry.parser.script.length > 0;\n}","tryCatchPattern":"try {\n  await provider.fetch(entry, ctx);\n} catch (err) {\n  if (err.message.includes('requires an in-repo parser script')) console.warn(`add parser.script to ${entry.name}`);\n  throw err;\n}","preventionTips":["Whenever parser.command is an interpreter, always pair it with parser.script pointing at an in-repo file.","Validate the interpreter+script pairing at config load.","Do not try to pass inline code via parser.args — write a script file instead."],"tags":["config","local-parser","interpreter","code-execution-guard","security"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}