Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 19 additions & 12 deletions src/cli/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ export const SBOM_FORMAT_ALIASES: Record<string, SbomFormat> = {
"spdx2.3": "spdx",
};

function requireFlagValue(flag: string, value: string | undefined, noun: string): string {
if (value === undefined || value === "" || value.startsWith("-")) {
throw new Error(`${flag} requires a ${noun} argument`);
}
return value;
}

function parseSbomFormat(value: string | undefined): SbomFormat {
const valid = Object.keys(SBOM_FORMAT_ALIASES).join(", ");
if (value === undefined || value.startsWith("-")) {
Expand Down Expand Up @@ -229,19 +236,19 @@ export function parseArgs(argv: string[]): {
continue;
}
if (arg === FLAG_AUDIT_LOG) {
options.auditLog = argv[++i];
options.auditLog = requireFlagValue(FLAG_AUDIT_LOG, argv[++i], "path");
continue;
}
if (arg.startsWith(`${FLAG_AUDIT_LOG}=`)) {
options.auditLog = arg.slice(FLAG_AUDIT_LOG.length + 1);
options.auditLog = requireFlagValue(FLAG_AUDIT_LOG, arg.slice(FLAG_AUDIT_LOG.length + 1), "path");
continue;
}
if (arg === FLAG_RULE) {
options.rule = argv[++i];
options.rule = requireFlagValue(FLAG_RULE, argv[++i], "rule id");
continue;
}
if (arg.startsWith(`${FLAG_RULE}=`)) {
options.rule = arg.slice(FLAG_RULE.length + 1);
options.rule = requireFlagValue(FLAG_RULE, arg.slice(FLAG_RULE.length + 1), "rule id");
continue;
}
if (arg === FLAG_FAIL_ON) {
Expand Down Expand Up @@ -324,11 +331,11 @@ export function parseArgs(argv: string[]): {
continue;
}
if (arg === FLAG_OFFLINE_DB) {
options.offlineDb = argv[++i];
options.offlineDb = requireFlagValue(FLAG_OFFLINE_DB, argv[++i], "path");
continue;
}
if (arg.startsWith(`${FLAG_OFFLINE_DB}=`)) {
options.offlineDb = arg.slice(FLAG_OFFLINE_DB.length + 1);
options.offlineDb = requireFlagValue(FLAG_OFFLINE_DB, arg.slice(FLAG_OFFLINE_DB.length + 1), "path");
continue;
}
if (arg === FLAG_ALL) {
Expand All @@ -352,19 +359,19 @@ export function parseArgs(argv: string[]): {
continue;
}
if (arg === FLAG_CACHE_DIR) {
options.cacheDir = argv[++i];
options.cacheDir = requireFlagValue(FLAG_CACHE_DIR, argv[++i], "path");
continue;
}
if (arg.startsWith(`${FLAG_CACHE_DIR}=`)) {
options.cacheDir = arg.slice(FLAG_CACHE_DIR.length + 1);
options.cacheDir = requireFlagValue(FLAG_CACHE_DIR, arg.slice(FLAG_CACHE_DIR.length + 1), "path");
continue;
}
if (arg === FLAG_OSV_URL) {
options.osvUrl = argv[++i];
options.osvUrl = requireFlagValue(FLAG_OSV_URL, argv[++i], "url");
continue;
}
if (arg.startsWith(`${FLAG_OSV_URL}=`)) {
options.osvUrl = arg.slice(FLAG_OSV_URL.length + 1);
options.osvUrl = requireFlagValue(FLAG_OSV_URL, arg.slice(FLAG_OSV_URL.length + 1), "url");
continue;
}
if (arg === FLAG_ALLOW_PRIVATE_OSV_URL) {
Expand Down Expand Up @@ -469,11 +476,11 @@ export function parseArgs(argv: string[]): {
continue;
}
if (arg === FLAG_AUDIT_LOG) {
options.auditLog = argv[++i];
options.auditLog = requireFlagValue(FLAG_AUDIT_LOG, argv[++i], "path");
continue;
}
if (arg.startsWith(`${FLAG_AUDIT_LOG}=`)) {
options.auditLog = arg.slice(FLAG_AUDIT_LOG.length + 1);
options.auditLog = requireFlagValue(FLAG_AUDIT_LOG, arg.slice(FLAG_AUDIT_LOG.length + 1), "path");
continue;
}
if (arg === FLAG_CHECK_OVERRIDES) {
Expand Down
74 changes: 74 additions & 0 deletions tests/cli/value-flag-args.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { parseArgs } from "../../src/cli/args.js";

const SCAN_VALUE_FLAGS: Array<{
flag: string;
noun: string;
option: "offlineDb" | "cacheDir" | "osvUrl" | "auditLog";
value: string;
}> = [
{ flag: "--offline-db", noun: "path", option: "offlineDb", value: "/tmp/advisories.db" },
{ flag: "--cache-dir", noun: "path", option: "cacheDir", value: "/tmp/cache" },
{ flag: "--osv-url", noun: "url", option: "osvUrl", value: "https://example.com/osv" },
{ flag: "--audit-log", noun: "path", option: "auditLog", value: "/tmp/audit.ndjson" },
];

const OVERRIDES_VALUE_FLAGS: Array<{
flag: string;
noun: string;
option: "auditLog" | "rule";
value: string;
}> = [
{ flag: "--audit-log", noun: "path", option: "auditLog", value: "/tmp/audit.ndjson" },
{ flag: "--rule", noun: "rule id", option: "rule", value: "OA001" },
];

describe("parseArgs - required value flags (scan)", () => {
it.each(SCAN_VALUE_FLAGS)(
"rejects $flag when the next token is another flag",
({ flag, noun }) => {
expect(() => parseArgs([".", flag, "--json"])).toThrow(`${flag} requires a ${noun} argument`);
},
);

it.each(SCAN_VALUE_FLAGS)("rejects $flag when it is missing at the end", ({ flag, noun }) => {
expect(() => parseArgs([".", flag])).toThrow(`${flag} requires a ${noun} argument`);
});

it.each(SCAN_VALUE_FLAGS)("rejects $flag with an empty = value", ({ flag, noun }) => {
expect(() => parseArgs([".", `${flag}=`])).toThrow(`${flag} requires a ${noun} argument`);
});

it.each(SCAN_VALUE_FLAGS)("parses $flag with a value in both forms", ({ flag, option, value }) => {
expect(parseArgs([".", flag, value]).options[option]).toBe(value);
expect(parseArgs([".", `${flag}=${value}`]).options[option]).toBe(value);
});
});

describe("parseArgs - required value flags (overrides)", () => {
it.each(OVERRIDES_VALUE_FLAGS)(
"rejects $flag when the next token is another flag",
({ flag, noun }) => {
expect(() => parseArgs(["overrides", ".", flag, "--json"])).toThrow(
`${flag} requires a ${noun} argument`,
);
},
);

it.each(OVERRIDES_VALUE_FLAGS)("rejects $flag when it is missing at the end", ({ flag, noun }) => {
expect(() => parseArgs(["overrides", ".", flag])).toThrow(`${flag} requires a ${noun} argument`);
});

it.each(OVERRIDES_VALUE_FLAGS)("rejects $flag with an empty = value", ({ flag, noun }) => {
expect(() => parseArgs(["overrides", ".", `${flag}=`])).toThrow(
`${flag} requires a ${noun} argument`,
);
});

it.each(OVERRIDES_VALUE_FLAGS)(
"parses $flag with a value in both forms",
({ flag, option, value }) => {
expect(parseArgs(["overrides", ".", flag, value]).options[option]).toBe(value);
expect(parseArgs(["overrides", ".", `${flag}=${value}`]).options[option]).toBe(value);
},
);
});
Loading