santifer/career-ops · error · Error
local-parser: careers_url must be http(s)
Error message
local-parser: careers_url must be http(s): ${value} What it means
safeCareersUrl() accepts only http: and https: protocols when interpolating {careers_url} into a local parser's argv. A parseable URL with any other scheme (ftp:, file:, javascript:, data:, etc.) is rejected to keep subprocess arguments safe.
Solutions
- Change careers_url in portals.yml to start with https:// (preferred) or http://.
- Remove the `{careers_url}` placeholder from parser.args if the parser does not actually need the URL.
- Confirm the field is a public web URL, not a local file path — use parser.script pointing at an in-repo file instead.
- Wrap fetch() calls in try-catch and surface a clear config-validation message before scan runs.
Example fix
// before (portals.yml) careers_url: file:///home/user/jobs.html // after careers_url: https://acme.com/jobs.html
Defensive patterns
Strategy: validation
Validate before calling
const u = new URL(entry.careers_url);
if (u.protocol !== 'http:' && u.protocol !== 'https:') throw new Error(`${entry.name}: careers_url must be http(s), got ${u.protocol}`); Try / catch
try {
await localParser.fetch(entry);
} catch (e) {
if (String(e.message).includes('careers_url must be http(s)')) {
console.error(`${entry.name}: use a public web URL (https://), not a file/ftp path`);
return [];
}
throw e;
} Prevention
- Treat careers_url as a public web address only — never file:// or internal schemes.
- Prefer https:// in all portal entries.
- Lint portals.yml for protocol allow-listing before committing config changes.
- Keep local file references in parser.script, not careers_url.
When it happens
Trigger: An entry's careers_url parses via `new URL()` but its protocol is not http/https — e.g. careers_url: file:///etc/passwd or ftp://acme.com/jobs — and a parser arg references `{careers_url}`.
Common situations: Local file paths pasted into careers_url (file:// or bare paths are caught here after URL-parsing quirks); internal ftp links in older configs; someone experimenting with javascript:/data: URLs.
Understand the failure class
Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.
Related errors
- arbeitnow: URL must use HTTPS
- eightfold: URL must use HTTPS
- justjoin: untrusted hostname
- manfred: URL must use HTTPS
- refusing non-GitHub/unsafe repo URL
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/a1e552d9b8b3d801.
Report an issue: GitHub.
Appendix: source
Thrown at providers/local-parser.mjs:32
// `parser.command` / `parser.script` come from portals.yml, which on a shared or
// template config is not fully trusted. The command must be a known interpreter
// or a file inside this project — never an arbitrary binary like `rm` or `curl`.
const PROJECT_ROOT = realpathSync(resolve(fileURLToPath(new URL('..', import.meta.url))));
const ALLOWED_INTERPRETERS = new Set(['python3', 'python', 'node', 'deno', 'bun', 'sh', 'bash']);
// `{careers_url}` and `{company}` are interpolated into the parser's argv. Validate
// them so an interpolated value can never be read as a CLI flag (argument injection).
function safeCareersUrl(value) {
if (!value) return '';
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.View on GitHub (pinned to aac998c7ed)