diff --git a/src/cli.ts b/src/cli.ts index 9be84c1..9e52755 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -14,22 +14,33 @@ import { ALL_SEVERITIES } from "./types.js"; const VERSION = "0.1.0"; +type CliFlag = string | boolean | string[]; + interface CliArgs { command?: string; positional: string[]; - flags: Record; + flags: Record; } -function parseArgs(argv: string[]): CliArgs { +export function parseArgs(argv: string[]): CliArgs { const positional: string[] = []; - const flags: Record = {}; + const flags: Record = {}; for (let i = 0; i < argv.length; i++) { const arg = argv[i]; if (arg.startsWith("--")) { const key = arg.slice(2); const next = argv[i + 1]; if (next !== undefined && !next.startsWith("--")) { - flags[key] = next; + if (key === "header" && flags[key] !== undefined) { + const previous = flags[key]; + flags[key] = Array.isArray(previous) + ? [...previous, next] + : typeof previous === "string" + ? [previous, next] + : next; + } else { + flags[key] = next; + } i++; } else { flags[key] = true; @@ -73,7 +84,7 @@ EXIT CODES `; } -function csv(value: string | boolean | undefined): string[] { +function csv(value: CliFlag | undefined): string[] { if (typeof value !== "string") return []; return value .split(",") @@ -83,7 +94,7 @@ function csv(value: string | boolean | undefined): string[] { function overlayFlags( base: McpAuditConfig, - flags: Record, + flags: Record, ): McpAuditConfig { const overlay: Record = {}; if (flags["fail-on"]) { @@ -100,8 +111,8 @@ function overlayFlags( return normalizeConfig(overlay, base); } -function collectHeaders( - flags: Record, +export function collectHeaders( + flags: Record, ): Record { const headers: Record = {}; const raw = flags["header"]; diff --git a/test/cli.test.ts b/test/cli.test.ts new file mode 100644 index 0000000..f4f1cf8 --- /dev/null +++ b/test/cli.test.ts @@ -0,0 +1,44 @@ +import { describe, expect, it } from "vitest"; +import { collectHeaders, parseArgs } from "../src/cli.js"; + +describe("HTTP headers", () => { + it("collects every repeated --header flag", () => { + const { flags } = parseArgs([ + "http", + "https://example.com/mcp", + "--header", + "X-Tenant: acme", + "--header", + "X-Env: prod", + ]); + + expect(collectHeaders(flags)).toEqual({ + "X-Tenant": "acme", + "X-Env": "prod", + }); + }); + + it("preserves colons in header values", () => { + const { flags } = parseArgs([ + "http", + "https://example.com/mcp", + "--header", + "Authorization: Bearer a:b", + ]); + + expect(collectHeaders(flags)).toEqual({ Authorization: "Bearer a:b" }); + }); + + it("keeps last-wins behavior for repeated non-header flags", () => { + const { flags } = parseArgs([ + "http", + "https://example.com/mcp", + "--config", + "one.json", + "--config", + "two.json", + ]); + + expect(flags.config).toBe("two.json"); + }); +});