santifer/career-ops · error · Error

local-parser: company name cannot start with '-': ${value}

Error message

local-parser: company name cannot start with '-': ${value}

What it means

safeCompany rejects an entry.name that, after trimming, starts with '-'. Because the company name is interpolated into the parser's argv and execFile passes args verbatim (no shell), the only remaining injection vector is a value that looks like a CLI flag (e.g. --eval, -c). This guard closes it.

Source

Thrown at providers/local-parser.mjs:43

  let url;
  try {
    url = new URL(String(value));
  } catch {
    throw new Error(`local-parser: careers_url is not a valid URL: ${value}`);
  }
  if (url.protocol !== 'http:' && url.protocol !== 'https:') {
    throw new Error(`local-parser: careers_url must be http(s): ${value}`);
  }
  return url.href;
}

function safeCompany(value) {
  if (!value) return '';
  const name = String(value).trim();
  // execFile passes args verbatim (no shell), so the only injection risk is a
  // value that begins like a CLI flag.
  if (name.startsWith('-')) {
    throw new Error(`local-parser: company name cannot start with '-': ${value}`);
  }
  return name;
}

// Only validate a placeholder's value when the arg actually uses it — a fixed
// `parser.script` must not be rejected because some unrelated `{company}` value
// has punctuation it never sees.
function expandParserArg(value, entry) {
  let out = String(value);
  if (out.includes('{careers_url}')) out = out.replaceAll('{careers_url}', safeCareersUrl(entry.careers_url));
  if (out.includes('{company}')) out = out.replaceAll('{company}', safeCompany(entry.name));
  return out;
}

function getParserScriptPath(entry) {
  const parser = entry.parser || {};
  if (parser.script) return expandParserArg(parser.script, entry);

View on GitHub (pinned to 9b17a8ac97)

Solutions

  1. Rename the entry so the company name does not start with '-' (e.g. 'Acme' instead of '-Acme').
  2. If the dash is intentional (e.g. '-8' brand), prefix with a non-dash character or wrap the value differently in the parser script rather than via argv.
  3. Remove the {company} placeholder from parser.args if the parser does not need it.

Example fix

# before
- name: '-Acme'
  parser: { command: python3, script: parsers/acme.py, args: ['{company}'] }

# after
- name: 'Acme'
  parser: { command: python3, script: parsers/acme.py, args: ['{company}'] }
Defensive patterns

Strategy: validation

Validate before calling

export function isSafeCompanyName(value) {
  return typeof value === 'string' && value.trim().length > 0 && !value.trim().startsWith('-');
}
// if (entry.parser?.args?.some(a => String(a).includes('{company}')) && !isSafeCompanyName(entry.name)) failConfig(...);

Type guard

/** @param {unknown} v */
function isFlagSafeName(v) {
  return typeof v === 'string' && v.trim().length > 0 && !v.trim().startsWith('-');
}

Try / catch

try {
  await provider.fetch(entry, ctx);
} catch (err) {
  if (err.message.includes("cannot start with '-'")) console.warn(`rename entry ${entry.name}: ${err.message}`);
  throw err;
}

Prevention

When it happens

Trigger: entry.name (or any value substituted into {company}) begins with '-' after trimming — e.g. '-Acme', '--help', '-c import os'. Only checked when the parser arg template actually contains {company}.

Common situations: A test/placeholder entry name like '-company'; a malformed YAML value where a flag-like token leaked into the name field; an adversarial or copy-pasted name beginning with a dash.

Related errors


AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13). Data as JSON: /api/errors/1b64dc73f83d1914. Report an issue: GitHub.