{"record":{"id":"1b64dc73f83d1914","repo":"santifer/career-ops","slug":"local-parser-company-name-cannot-start-with","errorCode":null,"errorMessage":"local-parser: company name cannot start with '-': ${value}","messagePattern":"local-parser: company name cannot start with '-': (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/local-parser.mjs","lineNumber":43,"sourceCode":"  let url;\n  try {\n    url = new URL(String(value));\n  } catch {\n    throw new Error(`local-parser: careers_url is not a valid URL: ${value}`);\n  }\n  if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n    throw new Error(`local-parser: careers_url must be http(s): ${value}`);\n  }\n  return url.href;\n}\n\nfunction safeCompany(value) {\n  if (!value) return '';\n  const name = String(value).trim();\n  // execFile passes args verbatim (no shell), so the only injection risk is a\n  // value that begins like a CLI flag.\n  if (name.startsWith('-')) {\n    throw new Error(`local-parser: company name cannot start with '-': ${value}`);\n  }\n  return name;\n}\n\n// Only validate a placeholder's value when the arg actually uses it — a fixed\n// `parser.script` must not be rejected because some unrelated `{company}` value\n// has punctuation it never sees.\nfunction expandParserArg(value, entry) {\n  let out = String(value);\n  if (out.includes('{careers_url}')) out = out.replaceAll('{careers_url}', safeCareersUrl(entry.careers_url));\n  if (out.includes('{company}')) out = out.replaceAll('{company}', safeCompany(entry.name));\n  return out;\n}\n\nfunction getParserScriptPath(entry) {\n  const parser = entry.parser || {};\n  if (parser.script) return expandParserArg(parser.script, entry);\n","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/providers/local-parser.mjs#L25-L61","documentation":"safeCompany() validates the {company} placeholder before argv interpolation. Because execFile passes args verbatim (no shell), the only injection risk is a value that looks like a CLI flag; a company name starting with '-' would be read as an option by the parser process, so it is rejected.","triggerScenarios":"A portals.yml entry whose parser args contain `{company}` and whose entry.name (after trim) starts with '-', e.g. name: ' - Acme' or a name like '-404 Studio'.","commonSituations":"Leading dash/hyphen from copy-paste artifacts (bullets, dashes); negative-number-like slugs used as company identifiers; YAML values that accidentally begin with '-' due to quoting mistakes.","solutions":["Rename the entry in portals.yml so name does not begin with '-', or strip the leading character.","Quote the YAML value properly so a leading dash is part of the intended name only if you also change it (this validator will still reject it — reword instead).","Move the flag-like token out of the company name, e.g. 'Acme-404' instead of '-404 Acme'.","If the parser genuinely needs a flag-like value, pass it via a dedicated non-interpolated parser arg, not via {company}."],"exampleFix":"// before (portals.yml)\n- name: \"-404 Studio\"\n  parser: {command: node, args: [\"parsers/jobs.js\", \"{company}\"]}\n// after\n- name: \"Studio 404\"\n  parser: {command: node, args: [\"parsers/jobs.js\", \"{company}\"]}","handlingStrategy":"validation","validationCode":"const name = String(entry.name || '').trim();\nif (name.startsWith('-')) throw new Error(`${entry.name}: company name must not start with '-' (would be read as a CLI flag)`);","typeGuard":null,"tryCatchPattern":"try {\n  await localParser.fetch(entry);\n} catch (e) {\n  if (String(e.message).includes(\"cannot start with '-'\")) {\n    console.error(`Rename ${entry.name}: leading '-' looks like a CLI flag to the parser process`);\n    return [];\n  }\n  throw e;\n}","preventionTips":["Sanitize company names when generating portals.yml programmatically (trim, strip leading dashes).","Quote YAML values, but reword rather than keep leading punctuation.","Avoid using slugs or negative-number-like tokens as entry names.","Review copy-pasted entries for stray bullet/dash characters."],"tags":["validation","security","argument-injection"],"backgroundTag":"invalid-argument-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"}