@quantakrypto/qscan 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/args.d.ts CHANGED
@@ -51,6 +51,12 @@ export interface QscanOptions {
51
51
  maxFileSize?: number;
52
52
  /** Disable the built-in ignore list (`--no-default-ignores`). */
53
53
  noDefaultIgnores: boolean;
54
+ /** Compliance mandates to gate findings against (repeatable `--mandate <id>`). */
55
+ mandates: string[];
56
+ /** Fail early when a mandate deadline is within this many months (`--lead-months`). */
57
+ leadMonths?: number;
58
+ /** Fail on any mandate-prohibited finding now, ignoring the deadline (`--fail-now`). */
59
+ failNow: boolean;
54
60
  /** Scan minified/generated/bundled files instead of skipping them. */
55
61
  scanMinified: boolean;
56
62
  /**
@@ -135,6 +141,26 @@ export interface QscanOptions {
135
141
  * from `--no-config` (which skips config-file *detectors*).
136
142
  */
137
143
  noConfigFile: boolean;
144
+ /**
145
+ * Compute + emit HNDL (harvest-now-decrypt-later) exposure (`--hndl`). Reads
146
+ * `hndl.yml` at the scan root, scores each finding + a repo summary, and adds
147
+ * the exposure fields to the JSON / SARIF report. Additive: never changes the
148
+ * exit code. See docs/HNDL.md.
149
+ */
150
+ hndl: boolean;
151
+ /**
152
+ * URL to a posture credential recorded in a crypto-agility manifest
153
+ * (`--attestation`). Only consumed by `crypto-agility emit`; recorded verbatim,
154
+ * never fetched (offline boundary). See docs/CRYPTO-AGILITY-MANIFEST.md.
155
+ */
156
+ attestation?: string;
157
+ /**
158
+ * Operator assertion of hybrid post-quantum key exchange for a crypto-agility
159
+ * manifest (`--hybrid-kex` → true, `--no-hybrid-kex` → false). Left undefined,
160
+ * the manifest reports `hybridKexInUse: null` (a static scan cannot observe a
161
+ * negotiated TLS group). Only consumed by `crypto-agility emit`.
162
+ */
163
+ hybridKexInUse?: boolean;
138
164
  }
139
165
  /**
140
166
  * Option keys that a `quantakrypto.config.json` may also set. When such a key was set
@@ -150,8 +176,28 @@ export interface ParsedRun {
150
176
  /** The set of {@link ConfigurableKey}s the user set via a flag. */
151
177
  explicit: Set<ConfigurableKey>;
152
178
  }
179
+ /** The `qscan hndl init` subcommand: scaffold an `hndl.yml`. */
180
+ export interface ParsedHndlInit {
181
+ kind: "hndl-init";
182
+ options: QscanOptions;
183
+ }
184
+ /**
185
+ * Emit a crypto-agility manifest: either `qscan crypto-agility emit [path]` or the
186
+ * `--crypto-agility` flag on a normal scan. Additive: it always exits 0, never
187
+ * consulting the severity threshold.
188
+ */
189
+ export interface ParsedCryptoAgilityEmit {
190
+ kind: "crypto-agility-emit";
191
+ options: QscanOptions;
192
+ }
193
+ /** Validate a local manifest file: `qscan crypto-agility validate <file>`. */
194
+ export interface ParsedCryptoAgilityValidate {
195
+ kind: "crypto-agility-validate";
196
+ /** Path to the manifest JSON file to check. */
197
+ file: string;
198
+ }
153
199
  /** Result of {@link parseArgs}: either resolved options or a meta action. */
154
- export type ParsedArgs = ParsedRun | {
200
+ export type ParsedArgs = ParsedRun | ParsedHndlInit | ParsedCryptoAgilityEmit | ParsedCryptoAgilityValidate | {
155
201
  kind: "help";
156
202
  } | {
157
203
  kind: "version";
@@ -1 +1 @@
1
- {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,cAAc,EACd,cAAc,EACd,YAAY,EAEb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC7F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAM9C,qCAAqC;AACrC,QAAA,MAAM,aAAa,6CAA8C,CAAC;AAClE,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAMzD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC;AAExD;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,MAAM,GAAG,UAAU,GAAG,KAAK,CAAC;AAIrE,sEAAsE;AACtE,MAAM,WAAW,YAAY;IAC3B,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,MAAM,EAAE,WAAW,CAAC;IACpB,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kFAAkF;IAClF,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,gEAAgE;IAChE,iBAAiB,EAAE,QAAQ,CAAC;IAC5B,iDAAiD;IACjD,MAAM,EAAE,OAAO,CAAC;IAChB,0DAA0D;IAC1D,YAAY,EAAE,OAAO,CAAC;IACtB,4CAA4C;IAC5C,MAAM,EAAE,OAAO,CAAC;IAChB,sDAAsD;IACtD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB;;;OAGG;IACH,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,gBAAgB,EAAE,OAAO,CAAC;IAC1B,sEAAsE;IACtE,YAAY,EAAE,OAAO,CAAC;IACtB;;;OAGG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,QAAQ,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6EAA6E;IAC7E,KAAK,EAAE,OAAO,CAAC;IACf;;;;OAIG;IACH,WAAW,EAAE,WAAW,CAAC;IACzB,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qFAAqF;IACrF,MAAM,EAAE,OAAO,CAAC;IAChB,0FAA0F;IAC1F,WAAW,CAAC,EAAE,QAAQ,CAAC;IACvB,kFAAkF;IAClF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qFAAqF;IACrF,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uFAAuF;IACvF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0FAA0F;IAC1F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kFAAkF;IAClF,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,0FAA0F;IAC1F,MAAM,EAAE,OAAO,CAAC;IAChB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,qCAAqC;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,UAAU,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GACvB,mBAAmB,GACnB,QAAQ,GACR,cAAc,GACd,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,aAAa,GACb,kBAAkB,GAClB,cAAc,GACd,UAAU,CAAC;AAEf,uFAAuF;AACvF,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,KAAK,CAAC;IACZ,OAAO,EAAE,YAAY,CAAC;IACtB,mEAAmE;IACnE,QAAQ,EAAE,GAAG,CAAC,eAAe,CAAC,CAAC;CAChC;AAED,6EAA6E;AAC7E,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAE5E,mEAAmE;AACnE,qBAAa,QAAS,SAAQ,KAAK;IACjC,SAAkB,IAAI,cAAc;CACrC;AAED,qDAAqD;AACrD,wBAAgB,cAAc,IAAI,YAAY,CAqB7C;AAED;;;;GAIG;AACH,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,6BAA6B,CAAC;AAE7D,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,CA8O7D;AAED,6CAA6C;AAC7C,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAGnD;AA+BD,4DAA4D;AAC5D,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAKzD;AAED,2CAA2C;AAC3C,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAGlD"}
1
+ {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
package/dist/args.js CHANGED
@@ -45,6 +45,9 @@ export function defaultOptions() {
45
45
  noConfigFile: false,
46
46
  triage: false,
47
47
  dryRun: false,
48
+ hndl: false,
49
+ mandates: [],
50
+ failNow: false,
48
51
  };
49
52
  }
50
53
  /**
@@ -55,9 +58,72 @@ export function defaultOptions() {
55
58
  /** Default scan-cache file when `--cache` is given without a path. */
56
59
  export const DEFAULT_CACHE_FILE = ".quantakrypto-cache.json";
57
60
  export function parseArgs(argv) {
61
+ // Subcommand dispatch: `qscan hndl <sub> …`. Only `init` exists today. `--help`
62
+ // / `--version` anywhere still short-circuit to the meta actions (handled by
63
+ // the shared loop below), so `qscan hndl init --help` shows help.
64
+ if (argv[0] === "hndl") {
65
+ const sub = argv[1];
66
+ if (sub === undefined || sub.startsWith("-")) {
67
+ if (sub === "--help" || sub === "-h")
68
+ return { kind: "help" };
69
+ if (sub === "--version" || sub === "-v")
70
+ return { kind: "version" };
71
+ throw new ArgError(`"hndl" requires a subcommand (expected: init)`);
72
+ }
73
+ if (sub !== "init") {
74
+ throw new ArgError(`unknown hndl subcommand "${sub}" (expected: init)`);
75
+ }
76
+ const parsed = parseRunArgs(argv.slice(2));
77
+ if (parsed.kind !== "run")
78
+ return parsed; // --help / --version passthrough
79
+ return { kind: "hndl-init", options: parsed.options };
80
+ }
81
+ // Subcommand dispatch: `qscan crypto-agility <sub> …`.
82
+ // emit [path] derive + write a manifest (always exits 0)
83
+ // validate <file> check a local manifest against the schema
84
+ if (argv[0] === "crypto-agility") {
85
+ const sub = argv[1];
86
+ if (sub === undefined || sub.startsWith("-")) {
87
+ if (sub === "--help" || sub === "-h")
88
+ return { kind: "help" };
89
+ if (sub === "--version" || sub === "-v")
90
+ return { kind: "version" };
91
+ throw new ArgError(`"crypto-agility" requires a subcommand (expected: emit, validate)`);
92
+ }
93
+ if (sub === "emit") {
94
+ const parsed = parseRunArgs(argv.slice(2));
95
+ // `--help`/`--version` short-circuit; a redundant `--crypto-agility` flag on
96
+ // the emit subcommand already yields the emit kind (returned as-is).
97
+ if (parsed.kind !== "run")
98
+ return parsed;
99
+ return { kind: "crypto-agility-emit", options: parsed.options };
100
+ }
101
+ if (sub === "validate") {
102
+ const rest = argv.slice(2);
103
+ if (rest.includes("--help") || rest.includes("-h"))
104
+ return { kind: "help" };
105
+ if (rest.includes("--version") || rest.includes("-v"))
106
+ return { kind: "version" };
107
+ const positionals = rest.filter((a) => !(a.startsWith("-") && a !== "-"));
108
+ if (positionals.length === 0) {
109
+ throw new ArgError(`"crypto-agility validate" requires a manifest file path`);
110
+ }
111
+ if (positionals.length > 1) {
112
+ throw new ArgError(`"crypto-agility validate" takes exactly one file (got ${positionals.length})`);
113
+ }
114
+ return { kind: "crypto-agility-validate", file: positionals[0] };
115
+ }
116
+ throw new ArgError(`unknown crypto-agility subcommand "${sub}" (expected: emit, validate)`);
117
+ }
118
+ return parseRunArgs(argv);
119
+ }
120
+ /** Parse the run-mode grammar (flags + a single optional positional path). */
121
+ function parseRunArgs(argv) {
58
122
  const options = defaultOptions();
59
123
  const explicit = new Set();
60
124
  let positional;
125
+ // Set by `--crypto-agility`: the whole run becomes a manifest emit (exit 0).
126
+ let emitManifest = false;
61
127
  // Manual index walk so flags can consume the following token as a value.
62
128
  for (let i = 0; i < argv.length; i++) {
63
129
  const arg = argv[i];
@@ -163,6 +229,16 @@ export function parseArgs(argv) {
163
229
  case "--profile":
164
230
  options.profile = asProfile(takeValue());
165
231
  break;
232
+ case "--mandate":
233
+ options.mandates.push(takeValue());
234
+ break;
235
+ case "--lead-months":
236
+ options.leadMonths = asInt(takeValue(), "--lead-months");
237
+ break;
238
+ case "--fail-now":
239
+ rejectInlineValue();
240
+ options.failNow = true;
241
+ break;
166
242
  case "--context":
167
243
  options.contextLevel = asContextLevel(takeValue());
168
244
  break;
@@ -266,6 +342,26 @@ export function parseArgs(argv) {
266
342
  rejectInlineValue();
267
343
  options.noSnippets = true;
268
344
  break;
345
+ case "--hndl":
346
+ rejectInlineValue();
347
+ options.hndl = true;
348
+ break;
349
+ // Crypto-agility manifest controls (see `crypto-agility emit`).
350
+ case "--crypto-agility":
351
+ rejectInlineValue();
352
+ emitManifest = true;
353
+ break;
354
+ case "--attestation":
355
+ options.attestation = takeValue();
356
+ break;
357
+ case "--hybrid-kex":
358
+ rejectInlineValue();
359
+ options.hybridKexInUse = true;
360
+ break;
361
+ case "--no-hybrid-kex":
362
+ rejectInlineValue();
363
+ options.hybridKexInUse = false;
364
+ break;
269
365
  default:
270
366
  if (flag.startsWith("-") && flag !== "-") {
271
367
  throw new ArgError(`unknown option "${flag}"`);
@@ -279,6 +375,10 @@ export function parseArgs(argv) {
279
375
  }
280
376
  if (positional !== undefined)
281
377
  options.path = positional;
378
+ // `--crypto-agility` turns the whole run into a manifest emit, taking it off the
379
+ // findings-threshold exit path entirely (the emit always exits 0).
380
+ if (emitManifest)
381
+ return { kind: "crypto-agility-emit", options };
282
382
  return { kind: "run", options, explicit };
283
383
  }
284
384
  /** Validate/normalize a `--format` value. */
package/dist/args.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,cAAc,EACd,cAAc,EACd,YAAY,EACZ,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAI5B,qFAAqF;AACrF,MAAM,cAAc,GAA4B,CAAC,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;AAC5F,iFAAiF;AACjF,MAAM,cAAc,GAA4B,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;AAC7E,qCAAqC;AACrC,MAAM,aAAa,GAAG,CAAC,WAAW,EAAE,mBAAmB,CAAU,CAAC;AAGlE,4EAA4E;AAC5E,2EAA2E;AAC3E,iFAAiF;AACjF,wDAAwD;AACxD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC;AAWxD,MAAM,OAAO,GAA2B,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC;AAkJ9F,mEAAmE;AACnE,MAAM,OAAO,QAAS,SAAQ,KAAK;IACf,IAAI,GAAG,UAAU,CAAC;CACrC;AAED,qDAAqD;AACrD,MAAM,UAAU,cAAc;IAC5B,OAAO;QACL,IAAI,EAAE,GAAG;QACT,MAAM,EAAE,OAAO;QACf,iBAAiB,EAAE,MAAM;QACzB,MAAM,EAAE,IAAI;QACZ,YAAY,EAAE,IAAI;QAClB,MAAM,EAAE,IAAI;QACZ,MAAM,EAAE,EAAE;QACV,OAAO,EAAE,EAAE;QACX,gBAAgB,EAAE,KAAK;QACvB,YAAY,EAAE,KAAK;QACnB,OAAO,EAAE,KAAK;QACd,QAAQ,EAAE,KAAK;QACf,KAAK,EAAE,KAAK;QACZ,WAAW,EAAE,MAAM;QACnB,UAAU,EAAE,KAAK;QACjB,YAAY,EAAE,KAAK;QACnB,MAAM,EAAE,KAAK;QACb,MAAM,EAAE,KAAK;KACd,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,sEAAsE;AACtE,MAAM,CAAC,MAAM,kBAAkB,GAAG,0BAA0B,CAAC;AAE7D,MAAM,UAAU,SAAS,CAAC,IAAuB;IAC/C,MAAM,OAAO,GAAG,cAAc,EAAE,CAAC;IACjC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAmB,CAAC;IAC5C,IAAI,UAA8B,CAAC;IAEnC,yEAAyE;IACzE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAW,CAAC;QAE9B,mDAAmD;QACnD,IAAI,WAA+B,CAAC;QACpC,IAAI,IAAI,GAAG,GAAG,CAAC;QACf,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC9C,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC5B,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACxB,WAAW,GAAG,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QAClC,CAAC;QAED,mFAAmF;QACnF,MAAM,SAAS,GAAG,GAAW,EAAE;YAC7B,IAAI,WAAW,KAAK,SAAS;gBAAE,OAAO,WAAW,CAAC;YAClD,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACzB,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;gBACjE,MAAM,IAAI,QAAQ,CAAC,WAAW,IAAI,oBAAoB,CAAC,CAAC;YAC1D,CAAC;YACD,CAAC,EAAE,CAAC;YACJ,OAAO,IAAI,CAAC;QACd,CAAC,CAAC;QAEF;;;;WAIG;QACH,MAAM,iBAAiB,GAAG,GAAS,EAAE;YACnC,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;gBAC9B,MAAM,IAAI,QAAQ,CAAC,WAAW,IAAI,wCAAwC,CAAC,CAAC;YAC9E,CAAC;QACH,CAAC,CAAC;QAEF,QAAQ,IAAI,EAAE,CAAC;YACb,KAAK,IAAI,CAAC;YACV,KAAK,QAAQ;gBACX,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;YAC1B,KAAK,IAAI,CAAC;YACV,KAAK,WAAW;gBACd,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;YAE7B,KAAK,UAAU;gBACb,OAAO,CAAC,MAAM,GAAG,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC;gBACvC,MAAM;YACR,KAAK,QAAQ;gBACX,iBAAiB,EAAE,CAAC;gBACpB,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC;gBACxB,MAAM;YACR,KAAK,SAAS;gBACZ,uEAAuE;gBACvE,6CAA6C;gBAC7C,CAAC,OAAO,CAAC,UAAU,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;gBAC9C,MAAM;YACR,KAAK,IAAI,CAAC;YACV,KAAK,UAAU;gBACb,OAAO,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;gBAC7B,MAAM;YACR,KAAK,sBAAsB;gBACzB,OAAO,CAAC,iBAAiB,GAAG,UAAU,CAAC,SAAS,EAAE,CAAC,CAAC;gBACpD,QAAQ,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;gBAClC,MAAM;YAER,KAAK,aAAa;gBAChB,iBAAiB,EAAE,CAAC;gBACpB,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC;gBACvB,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;gBACvB,MAAM;YACR,KAAK,WAAW;gBACd,iBAAiB,EAAE,CAAC;gBACpB,OAAO,CAAC,YAAY,GAAG,KAAK,CAAC;gBAC7B,QAAQ,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;gBAC7B,MAAM;YACR,KAAK,aAAa;gBAChB,iBAAiB,EAAE,CAAC;gBACpB,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC;gBACvB,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;gBACvB,MAAM;YAER,KAAK,UAAU;gBACb,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;gBACjC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;gBACvB,MAAM;YACR,KAAK,WAAW;gBACd,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;gBAClC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;gBACxB,MAAM;YACR,KAAK,iBAAiB;gBACpB,OAAO,CAAC,WAAW,GAAG,KAAK,CAAC,SAAS,EAAE,EAAE,iBAAiB,CAAC,CAAC;gBAC5D,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;gBAC5B,MAAM;YACR,KAAK,OAAO;gBACV,OAAO,CAAC,IAAI,GAAG,KAAK,CAAC,SAAS,EAAE,EAAE,OAAO,CAAC,CAAC;gBAC3C,MAAM;YACR,KAAK,UAAU;gBACb,iBAAiB,EAAE,CAAC;gBACpB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;gBACtB,MAAM;YACR,KAAK,gBAAgB;gBACnB,OAAO,CAAC,WAAW,GAAG,UAAU,CAAC,SAAS,EAAE,CAAC,CAAC;gBAC9C,MAAM;YACR,KAAK,gBAAgB;gBACnB,OAAO,CAAC,WAAW,GAAG,KAAK,CAAC,SAAS,EAAE,EAAE,gBAAgB,CAAC,CAAC;gBAC3D,MAAM;YACR,KAAK,QAAQ;gBACX,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;gBACnC,MAAM;YACR,KAAK,WAAW;gBACd,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,SAAS,EAAE,CAAC,CAAC;gBACzC,MAAM;YACR,KAAK,WAAW;gBACd,OAAO,CAAC,YAAY,GAAG,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC;gBACnD,MAAM;YACR,KAAK,WAAW;gBACd,iBAAiB,EAAE,CAAC;gBACpB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;gBACtB,MAAM;YACR,KAAK,gBAAgB;gBACnB,OAAO,CAAC,WAAW,GAAG,UAAU,CAAC,SAAS,EAAE,CAAC,CAAC;gBAC9C,MAAM;YACR,KAAK,aAAa;gBAChB,OAAO,CAAC,QAAQ,GAAG,SAAS,EAAE,CAAC;gBAC/B,MAAM;YACR,KAAK,SAAS,CAAC,CAAC,CAAC;gBACf,oEAAoE;gBACpE,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;oBAC9B,OAAO,CAAC,SAAS,GAAG,WAAW,CAAC;gBAClC,CAAC;qBAAM,CAAC;oBACN,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;oBACzB,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;wBAClE,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;wBACzB,CAAC,EAAE,CAAC;oBACN,CAAC;yBAAM,CAAC;wBACN,OAAO,CAAC,SAAS,GAAG,kBAAkB,CAAC;oBACzC,CAAC;gBACH,CAAC;gBACD,MAAM;YACR,CAAC;YACD,KAAK,sBAAsB;gBACzB,iBAAiB,EAAE,CAAC;gBACpB,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;gBAChC,QAAQ,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;gBACjC,MAAM;YACR,KAAK,iBAAiB;gBACpB,iBAAiB,EAAE,CAAC;gBACpB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;gBAC5B,QAAQ,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;gBAC7B,MAAM;YAER,+EAA+E;YAC/E,iDAAiD;YACjD,KAAK,UAAU;gBACb,OAAO,CAAC,UAAU,GAAG,SAAS,EAAE,CAAC;gBACjC,MAAM;YACR,KAAK,kBAAkB;gBACrB,iBAAiB,EAAE,CAAC;gBACpB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;gBAC5B,MAAM;YAER,KAAK,WAAW;gBACd,iBAAiB,EAAE,CAAC;gBACpB,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;gBACvB,MAAM;YACR,KAAK,SAAS;gBACZ,OAAO,CAAC,KAAK,GAAG,SAAS,EAAE,CAAC;gBAC5B,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC,mCAAmC;gBAC3D,MAAM;YAER,KAAK,YAAY;gBACf,iBAAiB,EAAE,CAAC;gBACpB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;gBACxB,MAAM;YACR,KAAK,eAAe,CAAC,CAAC,CAAC;gBACrB,MAAM,CAAC,GAAG,KAAK,CAAC,SAAS,EAAE,EAAE,eAAe,CAAC,CAAC;gBAC9C,OAAO,CAAC,WAAW,GAAG,CAAC,CAAC;gBACxB,uEAAuE;gBACvE,wEAAwE;gBACxE,yEAAyE;gBACzE,oBAAoB;gBACpB,OAAO,CAAC,QAAQ,GAAG,CAAC,IAAI,CAAC,CAAC;gBAC1B,MAAM;YACR,CAAC;YAED,KAAK,YAAY;gBACf,OAAO,CAAC,QAAQ,GAAG,SAAS,EAAE,CAAC;gBAC/B,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;gBACzB,MAAM;YACR,KAAK,UAAU;gBACb,OAAO,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;gBAC7B,MAAM;YACR,KAAK,QAAQ;gBACX,OAAO,CAAC,IAAI,GAAG,SAAS,EAAE,CAAC;gBAC3B,MAAM;YACR,KAAK,aAAa;gBAChB,OAAO,CAAC,SAAS,GAAG,SAAS,EAAE,CAAC;gBAChC,MAAM;YACR,KAAK,kBAAkB;gBACrB,OAAO,CAAC,aAAa,GAAG,SAAS,EAAE,CAAC;gBACpC,MAAM;YACR,KAAK,SAAS;gBACZ,iBAAiB,EAAE,CAAC;gBACpB,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;gBACrB,MAAM;YACR,KAAK,SAAS;gBACZ,iBAAiB,EAAE,CAAC;gBACpB,OAAO,CAAC,WAAW,GAAG,QAAQ,CAAC;gBAC/B,MAAM;YACR,KAAK,YAAY;gBACf,iBAAiB,EAAE,CAAC;gBACpB,OAAO,CAAC,WAAW,GAAG,OAAO,CAAC;gBAC9B,MAAM;YACR,KAAK,eAAe;gBAClB,iBAAiB,EAAE,CAAC;gBACpB,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;gBAC1B,MAAM;YAER;gBACE,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;oBACzC,MAAM,IAAI,QAAQ,CAAC,mBAAmB,IAAI,GAAG,CAAC,CAAC;gBACjD,CAAC;gBACD,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;oBAC7B,MAAM,IAAI,QAAQ,CAChB,8BAA8B,GAAG,2BAA2B,UAAU,IAAI,CAC3E,CAAC;gBACJ,CAAC;gBACD,UAAU,GAAG,GAAG,CAAC;gBACjB,MAAM;QACV,CAAC;IACH,CAAC;IAED,IAAI,UAAU,KAAK,SAAS;QAAE,OAAO,CAAC,IAAI,GAAG,UAAU,CAAC;IACxD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;AAC5C,CAAC;AAED,6CAA6C;AAC7C,MAAM,UAAU,QAAQ,CAAC,KAAa;IACpC,IAAK,OAA6B,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAoB,CAAC;IAChF,MAAM,IAAI,QAAQ,CAAC,qBAAqB,KAAK,uBAAuB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC7F,CAAC;AAED,8CAA8C;AAC9C,SAAS,cAAc,CAAC,KAAa;IACnC,IAAK,cAAoC,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAqB,CAAC;IACxF,MAAM,IAAI,QAAQ,CAChB,sBAAsB,KAAK,uBAAuB,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAC/E,CAAC;AACJ,CAAC;AAED,2CAA2C;AAC3C,SAAS,MAAM,CAAC,KAAa;IAC3B,IAAK,cAAoC,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAqB,CAAC;IACxF,MAAM,IAAI,QAAQ,CAAC,mBAAmB,KAAK,uBAAuB,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClG,CAAC;AAED,2EAA2E;AAC3E,SAAS,SAAS,CAAC,KAAa;IAC9B,MAAM,GAAG,GAAG,mBAAmB,EAAE,CAAC;IAClC,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACtC,MAAM,IAAI,QAAQ,CAAC,sBAAsB,KAAK,uBAAuB,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC1F,CAAC;AAED,mDAAmD;AACnD,SAAS,UAAU,CAAC,KAAa;IAC/B,IAAK,aAAmC,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAoB,CAAC;IACtF,MAAM,IAAI,QAAQ,CAChB,2BAA2B,KAAK,uBAAuB,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CACnF,CAAC;AACJ,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,KAAK,CAAC,KAAa,EAAE,IAAY;IAC/C,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,QAAQ,CAAC,WAAW,IAAI,KAAK,KAAK,qCAAqC,CAAC,CAAC;IACrF,CAAC;IACD,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACpC,CAAC;AAED,2CAA2C;AAC3C,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,IAAK,cAAoC,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAiB,CAAC;IACpF,MAAM,IAAI,QAAQ,CAAC,qBAAqB,KAAK,uBAAuB,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACpG,CAAC","sourcesContent":["/**\n * Zero-dependency command-line argument parsing for qScan.\n *\n * Hand-rolled rather than pulled from a library to keep the package free of\n * runtime dependencies. The grammar is deliberately small: long flags\n * (`--flag`, `--flag value`, `--flag=value`), a couple of short aliases\n * (`-o`, `-v`, `-h`), repeatable `--ignore`, and a single optional positional\n * path. Unknown flags are a usage error.\n */\n\nimport {\n meetsThreshold,\n SEVERITY_ORDER,\n severityRank,\n standardsProfileIds,\n} from \"@quantakrypto/core\";\nimport type { ContextLevel, ReportFormat, SecurityTier, Severity } from \"@quantakrypto/core\";\nimport type { ColorChoice } from \"./color.js\";\n\n/** Valid context levels for `--context` (how much source triage/remediate sends). */\nconst CONTEXT_LEVELS: readonly ContextLevel[] = [\"metadata\", \"snippet\", \"function\", \"file\"];\n/** Valid CNSA security tiers for `--tier` (report migration-target guidance). */\nconst SECURITY_TIERS: readonly SecurityTier[] = [\"category-3\", \"category-5\"];\n/** Valid `--llm-provider` values. */\nconst LLM_PROVIDERS = [\"anthropic\", \"openai-compatible\"] as const;\nexport type LlmProvider = (typeof LLM_PROVIDERS)[number];\n\n// Severity ordering, ranking, and threshold logic are the monorepo's single\n// source of truth in `@quantakrypto/core`. Re-export them here so existing\n// `@quantakrypto/qscan` callers (and tests) keep importing them from `./args.js`\n// without qScan maintaining a second, drift-prone copy.\nexport { meetsThreshold, SEVERITY_ORDER, severityRank };\n\n/**\n * Output formats qScan accepts on the command line. Extends core's\n * {@link ReportFormat} with `\"cbom\"` (a CycloneDX cryptographic bill of\n * materials), `\"evidence\"` (ISO A.8.24 readiness report), and `\"vex\"` (an\n * OpenVEX 0.2.0 document) — all rendered locally via core (`toCbom`,\n * `buildReadinessReport`, `toOpenVex`).\n */\nexport type QscanFormat = ReportFormat | \"cbom\" | \"evidence\" | \"vex\";\n\nconst FORMATS: readonly QscanFormat[] = [\"human\", \"json\", \"sarif\", \"cbom\", \"evidence\", \"vex\"];\n\n/** Fully-resolved options the CLI/programmatic runner operates on. */\nexport interface QscanOptions {\n /** Directory or file to scan. */\n path: string;\n /** Report format. */\n format: QscanFormat;\n /** Write the report to this file instead of stdout, when set. */\n output?: string;\n /** External CBOM files to merge into the `--cbom` output (CycloneDX bom-link). */\n mergeCboms?: string[];\n /** Findings at or above this severity cause a non-zero exit. */\n severityThreshold: Severity;\n /** Scan source files for inline crypto usage. */\n source: boolean;\n /** Scan dependency manifests for vulnerable libraries. */\n dependencies: boolean;\n /** Scan config files (TLS/certificates). */\n config: boolean;\n /** Extra exclude patterns (repeatable `--ignore`). */\n ignore: string[];\n /**\n * Restrict the walk to paths matching one of these include patterns\n * (repeatable `--include`). When empty, every non-excluded file is scanned.\n */\n include: string[];\n /** Max file size to read, in bytes (`--max-file-size`). */\n maxFileSize?: number;\n /** Disable the built-in ignore list (`--no-default-ignores`). */\n noDefaultIgnores: boolean;\n /** Scan minified/generated/bundled files instead of skipping them. */\n scanMinified: boolean;\n /**\n * Incremental mode: scan only the files git reports as changed\n * (`--changed`), optionally relative to {@link since}.\n */\n changed: boolean;\n /** Git ref/range the `--changed` diff is taken against (`--since`). */\n since?: string;\n /** Route the scan through core's worker-thread pool (`--parallel`). */\n parallel: boolean;\n /**\n * Worker count for parallel scanning (`--concurrency`). Implies parallel.\n * A value of 0 or 1 forces the in-process serial path.\n */\n concurrency?: number;\n /** Suppress findings whose fingerprint is in this baseline file. */\n baseline?: string;\n /** Write current findings as a baseline to this file, then exit 0. */\n writeBaseline?: string;\n /** Suppress the human summary banner (still writes reports/output files). */\n quiet: boolean;\n /**\n * ANSI color policy for the human report (`--color` / `--no-color`). `\"auto\"`\n * (default) colors only a live terminal and honors `NO_COLOR` / `FORCE_COLOR`;\n * see {@link resolveColor}. Color is decoration only — never the sole signal.\n */\n colorChoice: ColorChoice;\n /** How many findings the human report lists (`--top N`). Default: 5. */\n topN?: number;\n /** Rule ids to suppress (from `quantakrypto.config.json` `disabledRules`). */\n disabledRules?: string[];\n /** Content-hash scan cache path (`--cache [path]`); reuse unchanged files. */\n cacheFile?: string;\n /** Run the BYOK LLM triage pass (`--triage`): annotate + re-sort, never suppress. */\n triage: boolean;\n /** Only triage findings at/above this seriousness (`--triage-floor`). Default: medium. */\n triageFloor?: Severity;\n /** Cap findings sent to the LLM during triage (`--max-findings`; spend guard). */\n maxFindings?: number;\n /** CNSA security tier for the migration-targets footer (`--tier`). Default: none. */\n tier?: SecurityTier;\n /**\n * Standards regime the migration guidance is tailored to (`--profile`): one of the\n * built-in ids (nist / cnsa-2.0 / bsi-tr-02102 / anssi / uk-ncsc). Default: none\n * (`--tier` maps to a profile for back-compat). Governs parameter sets, deadlines,\n * and the hybrid stance surfaced in remediation.\n */\n profile?: string;\n /** Org cryptography policy file (`--policy`) for the evidence report's §4 verdicts. */\n policy?: string;\n /**\n * External signer command for the evidence attestation (`--sign`). The report's\n * contentHash is piped to it on stdin; its stdout is recorded as the detached\n * signature. Only valid with `--format evidence`.\n */\n sign?: string;\n /** External RFC-3161 timestamper command for the evidence attestation (`--timestamp`). */\n timestamp?: string;\n /** How much source context leaves the machine (`--context`). Default: snippet. */\n contextLevel?: ContextLevel;\n /** Print the exact triage payload and exit without calling the provider (`--dry-run`). */\n dryRun: boolean;\n /** BYOK provider (`--llm-provider`). Default: anthropic. */\n llmProvider?: LlmProvider;\n /** BYOK model id (`--llm-model`). */\n llmModel?: string;\n /**\n * Omit code snippets from the JSON/SARIF report (`--no-snippets`). Passed to\n * core's reporters as `{ redactSnippets: true }`. Snippets of `sensitive`\n * findings are always omitted regardless of this flag.\n */\n noSnippets: boolean;\n /**\n * Explicit path to a `quantakrypto.config.json` (`--config <path>`). Overrides\n * auto-discovery at the scan root. Distinct from `--no-config`, which toggles\n * config/TLS *detector* scanning — this names the config FILE.\n */\n configFile?: string;\n /**\n * Disable `quantakrypto.config.json` auto-discovery (`--no-config-file`). Distinct\n * from `--no-config` (which skips config-file *detectors*).\n */\n noConfigFile: boolean;\n}\n\n/**\n * Option keys that a `quantakrypto.config.json` may also set. When such a key was set\n * by a CLI flag, the flag wins (precedence: flags > config > defaults); when it\n * was left at its default, config may fill it. {@link parseArgs} records which\n * of these keys came from an explicit flag in {@link ParsedRun.explicit}.\n */\nexport type ConfigurableKey =\n | \"severityThreshold\"\n | \"source\"\n | \"dependencies\"\n | \"config\"\n | \"include\"\n | \"ignore\"\n | \"maxFileSize\"\n | \"noDefaultIgnores\"\n | \"scanMinified\"\n | \"baseline\";\n\n/** A successful parse: resolved options plus which configurable keys were explicit. */\nexport interface ParsedRun {\n kind: \"run\";\n options: QscanOptions;\n /** The set of {@link ConfigurableKey}s the user set via a flag. */\n explicit: Set<ConfigurableKey>;\n}\n\n/** Result of {@link parseArgs}: either resolved options or a meta action. */\nexport type ParsedArgs = ParsedRun | { kind: \"help\" } | { kind: \"version\" };\n\n/** Thrown on malformed input; the CLI maps this to exit code 2. */\nexport class ArgError extends Error {\n override readonly name = \"ArgError\";\n}\n\n/** Default options, before any flags are applied. */\nexport function defaultOptions(): QscanOptions {\n return {\n path: \".\",\n format: \"human\",\n severityThreshold: \"high\",\n source: true,\n dependencies: true,\n config: true,\n ignore: [],\n include: [],\n noDefaultIgnores: false,\n scanMinified: false,\n changed: false,\n parallel: false,\n quiet: false,\n colorChoice: \"auto\",\n noSnippets: false,\n noConfigFile: false,\n triage: false,\n dryRun: false,\n };\n}\n\n/**\n * Parse a raw argv slice (i.e. without `node` and the script path).\n *\n * @throws {ArgError} On unknown flags, missing values, or invalid enum values.\n */\n/** Default scan-cache file when `--cache` is given without a path. */\nexport const DEFAULT_CACHE_FILE = \".quantakrypto-cache.json\";\n\nexport function parseArgs(argv: readonly string[]): ParsedArgs {\n const options = defaultOptions();\n const explicit = new Set<ConfigurableKey>();\n let positional: string | undefined;\n\n // Manual index walk so flags can consume the following token as a value.\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i] as string;\n\n // `--flag=value` → split into flag + inline value.\n let inlineValue: string | undefined;\n let flag = arg;\n if (arg.startsWith(\"--\") && arg.includes(\"=\")) {\n const eq = arg.indexOf(\"=\");\n flag = arg.slice(0, eq);\n inlineValue = arg.slice(eq + 1);\n }\n\n /** Consume a value for `flag`: prefer the inline `=value`, else the next token. */\n const takeValue = (): string => {\n if (inlineValue !== undefined) return inlineValue;\n const next = argv[i + 1];\n if (next === undefined || (next.startsWith(\"-\") && next !== \"-\")) {\n throw new ArgError(`option \"${flag}\" requires a value`);\n }\n i++;\n return next;\n };\n\n /**\n * Reject an inline `=value` on a boolean flag. Without this, `--quiet=false`\n * would silently ignore the value and turn the flag ON — the opposite of the\n * caller's intent. Boolean flags take no value, so any `=value` is an error.\n */\n const rejectInlineValue = (): void => {\n if (inlineValue !== undefined) {\n throw new ArgError(`option \"${flag}\" is a boolean flag and takes no value`);\n }\n };\n\n switch (flag) {\n case \"-h\":\n case \"--help\":\n return { kind: \"help\" };\n case \"-v\":\n case \"--version\":\n return { kind: \"version\" };\n\n case \"--format\":\n options.format = asFormat(takeValue());\n break;\n case \"--cbom\":\n rejectInlineValue();\n options.format = \"cbom\";\n break;\n case \"--merge\":\n // Merge an external CBOM (e.g. a qprobe endpoint CBOM) into the --cbom\n // output via CycloneDX bom-link. Repeatable.\n (options.mergeCboms ??= []).push(takeValue());\n break;\n case \"-o\":\n case \"--output\":\n options.output = takeValue();\n break;\n case \"--severity-threshold\":\n options.severityThreshold = asSeverity(takeValue());\n explicit.add(\"severityThreshold\");\n break;\n\n case \"--no-source\":\n rejectInlineValue();\n options.source = false;\n explicit.add(\"source\");\n break;\n case \"--no-deps\":\n rejectInlineValue();\n options.dependencies = false;\n explicit.add(\"dependencies\");\n break;\n case \"--no-config\":\n rejectInlineValue();\n options.config = false;\n explicit.add(\"config\");\n break;\n\n case \"--ignore\":\n options.ignore.push(takeValue());\n explicit.add(\"ignore\");\n break;\n case \"--include\":\n options.include.push(takeValue());\n explicit.add(\"include\");\n break;\n case \"--max-file-size\":\n options.maxFileSize = asInt(takeValue(), \"--max-file-size\");\n explicit.add(\"maxFileSize\");\n break;\n case \"--top\":\n options.topN = asInt(takeValue(), \"--top\");\n break;\n case \"--triage\":\n rejectInlineValue();\n options.triage = true;\n break;\n case \"--triage-floor\":\n options.triageFloor = asSeverity(takeValue());\n break;\n case \"--max-findings\":\n options.maxFindings = asInt(takeValue(), \"--max-findings\");\n break;\n case \"--tier\":\n options.tier = asTier(takeValue());\n break;\n case \"--profile\":\n options.profile = asProfile(takeValue());\n break;\n case \"--context\":\n options.contextLevel = asContextLevel(takeValue());\n break;\n case \"--dry-run\":\n rejectInlineValue();\n options.dryRun = true;\n break;\n case \"--llm-provider\":\n options.llmProvider = asProvider(takeValue());\n break;\n case \"--llm-model\":\n options.llmModel = takeValue();\n break;\n case \"--cache\": {\n // `--cache` alone uses the default file; `--cache <path>` names it.\n if (inlineValue !== undefined) {\n options.cacheFile = inlineValue;\n } else {\n const next = argv[i + 1];\n if (next !== undefined && !(next.startsWith(\"-\") && next !== \"-\")) {\n options.cacheFile = next;\n i++;\n } else {\n options.cacheFile = DEFAULT_CACHE_FILE;\n }\n }\n break;\n }\n case \"--no-default-ignores\":\n rejectInlineValue();\n options.noDefaultIgnores = true;\n explicit.add(\"noDefaultIgnores\");\n break;\n case \"--scan-minified\":\n rejectInlineValue();\n options.scanMinified = true;\n explicit.add(\"scanMinified\");\n break;\n\n // `quantakrypto.config.json` FILE controls (distinct from `--no-config`, which\n // toggles config/TLS *detector* scanning above).\n case \"--config\":\n options.configFile = takeValue();\n break;\n case \"--no-config-file\":\n rejectInlineValue();\n options.noConfigFile = true;\n break;\n\n case \"--changed\":\n rejectInlineValue();\n options.changed = true;\n break;\n case \"--since\":\n options.since = takeValue();\n options.changed = true; // --since implies incremental mode\n break;\n\n case \"--parallel\":\n rejectInlineValue();\n options.parallel = true;\n break;\n case \"--concurrency\": {\n const n = asInt(takeValue(), \"--concurrency\");\n options.concurrency = n;\n // `--concurrency 0` documents \"serial\": core treats <1 as \"auto\" (full\n // parallelism), so without this special-case 0 would do the OPPOSITE of\n // what's documented. 0 forces the in-process serial path; any value >= 1\n // implies parallel.\n options.parallel = n >= 1;\n break;\n }\n\n case \"--baseline\":\n options.baseline = takeValue();\n explicit.add(\"baseline\");\n break;\n case \"--policy\":\n options.policy = takeValue();\n break;\n case \"--sign\":\n options.sign = takeValue();\n break;\n case \"--timestamp\":\n options.timestamp = takeValue();\n break;\n case \"--write-baseline\":\n options.writeBaseline = takeValue();\n break;\n case \"--quiet\":\n rejectInlineValue();\n options.quiet = true;\n break;\n case \"--color\":\n rejectInlineValue();\n options.colorChoice = \"always\";\n break;\n case \"--no-color\":\n rejectInlineValue();\n options.colorChoice = \"never\";\n break;\n case \"--no-snippets\":\n rejectInlineValue();\n options.noSnippets = true;\n break;\n\n default:\n if (flag.startsWith(\"-\") && flag !== \"-\") {\n throw new ArgError(`unknown option \"${flag}\"`);\n }\n if (positional !== undefined) {\n throw new ArgError(\n `unexpected extra argument \"${arg}\" (path already set to \"${positional}\")`,\n );\n }\n positional = arg;\n break;\n }\n }\n\n if (positional !== undefined) options.path = positional;\n return { kind: \"run\", options, explicit };\n}\n\n/** Validate/normalize a `--format` value. */\nexport function asFormat(value: string): QscanFormat {\n if ((FORMATS as readonly string[]).includes(value)) return value as QscanFormat;\n throw new ArgError(`invalid --format \"${value}\" (expected one of: ${FORMATS.join(\", \")})`);\n}\n\n/** Validate/normalize a `--context` level. */\nfunction asContextLevel(value: string): ContextLevel {\n if ((CONTEXT_LEVELS as readonly string[]).includes(value)) return value as ContextLevel;\n throw new ArgError(\n `invalid --context \"${value}\" (expected one of: ${CONTEXT_LEVELS.join(\", \")})`,\n );\n}\n\n/** Validate/normalize a `--tier` value. */\nfunction asTier(value: string): SecurityTier {\n if ((SECURITY_TIERS as readonly string[]).includes(value)) return value as SecurityTier;\n throw new ArgError(`invalid --tier \"${value}\" (expected one of: ${SECURITY_TIERS.join(\", \")})`);\n}\n\n/** Validate a `--profile` value against the built-in standards regimes. */\nfunction asProfile(value: string): string {\n const ids = standardsProfileIds();\n if (ids.includes(value)) return value;\n throw new ArgError(`invalid --profile \"${value}\" (expected one of: ${ids.join(\", \")})`);\n}\n\n/** Validate/normalize a `--llm-provider` value. */\nfunction asProvider(value: string): LlmProvider {\n if ((LLM_PROVIDERS as readonly string[]).includes(value)) return value as LlmProvider;\n throw new ArgError(\n `invalid --llm-provider \"${value}\" (expected one of: ${LLM_PROVIDERS.join(\", \")})`,\n );\n}\n\n/** Validate/normalize a non-negative integer flag value. */\nexport function asInt(value: string, flag: string): number {\n if (!/^\\d+$/.test(value)) {\n throw new ArgError(`invalid ${flag} \"${value}\" (expected a non-negative integer)`);\n }\n return Number.parseInt(value, 10);\n}\n\n/** Validate/normalize a severity value. */\nexport function asSeverity(value: string): Severity {\n if ((SEVERITY_ORDER as readonly string[]).includes(value)) return value as Severity;\n throw new ArgError(`invalid severity \"${value}\" (expected one of: ${SEVERITY_ORDER.join(\", \")})`);\n}\n"]}
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Zero-dependency command-line argument parsing for qScan.\n *\n * Hand-rolled rather than pulled from a library to keep the package free of\n * runtime dependencies. The grammar is deliberately small: long flags\n * (`--flag`, `--flag value`, `--flag=value`), a couple of short aliases\n * (`-o`, `-v`, `-h`), repeatable `--ignore`, and a single optional positional\n * path. Unknown flags are a usage error.\n */\n\nimport {\n meetsThreshold,\n SEVERITY_ORDER,\n severityRank,\n standardsProfileIds,\n} from \"@quantakrypto/core\";\nimport type { ContextLevel, ReportFormat, SecurityTier, Severity } from \"@quantakrypto/core\";\nimport type { ColorChoice } from \"./color.js\";\n\n/** Valid context levels for `--context` (how much source triage/remediate sends). */\nconst CONTEXT_LEVELS: readonly ContextLevel[] = [\"metadata\", \"snippet\", \"function\", \"file\"];\n/** Valid CNSA security tiers for `--tier` (report migration-target guidance). */\nconst SECURITY_TIERS: readonly SecurityTier[] = [\"category-3\", \"category-5\"];\n/** Valid `--llm-provider` values. */\nconst LLM_PROVIDERS = [\"anthropic\", \"openai-compatible\"] as const;\nexport type LlmProvider = (typeof LLM_PROVIDERS)[number];\n\n// Severity ordering, ranking, and threshold logic are the monorepo's single\n// source of truth in `@quantakrypto/core`. Re-export them here so existing\n// `@quantakrypto/qscan` callers (and tests) keep importing them from `./args.js`\n// without qScan maintaining a second, drift-prone copy.\nexport { meetsThreshold, SEVERITY_ORDER, severityRank };\n\n/**\n * Output formats qScan accepts on the command line. Extends core's\n * {@link ReportFormat} with `\"cbom\"` (a CycloneDX cryptographic bill of\n * materials), `\"evidence\"` (ISO A.8.24 readiness report), and `\"vex\"` (an\n * OpenVEX 0.2.0 document) — all rendered locally via core (`toCbom`,\n * `buildReadinessReport`, `toOpenVex`).\n */\nexport type QscanFormat = ReportFormat | \"cbom\" | \"evidence\" | \"vex\";\n\nconst FORMATS: readonly QscanFormat[] = [\"human\", \"json\", \"sarif\", \"cbom\", \"evidence\", \"vex\"];\n\n/** Fully-resolved options the CLI/programmatic runner operates on. */\nexport interface QscanOptions {\n /** Directory or file to scan. */\n path: string;\n /** Report format. */\n format: QscanFormat;\n /** Write the report to this file instead of stdout, when set. */\n output?: string;\n /** External CBOM files to merge into the `--cbom` output (CycloneDX bom-link). */\n mergeCboms?: string[];\n /** Findings at or above this severity cause a non-zero exit. */\n severityThreshold: Severity;\n /** Scan source files for inline crypto usage. */\n source: boolean;\n /** Scan dependency manifests for vulnerable libraries. */\n dependencies: boolean;\n /** Scan config files (TLS/certificates). */\n config: boolean;\n /** Extra exclude patterns (repeatable `--ignore`). */\n ignore: string[];\n /**\n * Restrict the walk to paths matching one of these include patterns\n * (repeatable `--include`). When empty, every non-excluded file is scanned.\n */\n include: string[];\n /** Max file size to read, in bytes (`--max-file-size`). */\n maxFileSize?: number;\n /** Disable the built-in ignore list (`--no-default-ignores`). */\n noDefaultIgnores: boolean;\n /** Compliance mandates to gate findings against (repeatable `--mandate <id>`). */\n mandates: string[];\n /** Fail early when a mandate deadline is within this many months (`--lead-months`). */\n leadMonths?: number;\n /** Fail on any mandate-prohibited finding now, ignoring the deadline (`--fail-now`). */\n failNow: boolean;\n /** Scan minified/generated/bundled files instead of skipping them. */\n scanMinified: boolean;\n /**\n * Incremental mode: scan only the files git reports as changed\n * (`--changed`), optionally relative to {@link since}.\n */\n changed: boolean;\n /** Git ref/range the `--changed` diff is taken against (`--since`). */\n since?: string;\n /** Route the scan through core's worker-thread pool (`--parallel`). */\n parallel: boolean;\n /**\n * Worker count for parallel scanning (`--concurrency`). Implies parallel.\n * A value of 0 or 1 forces the in-process serial path.\n */\n concurrency?: number;\n /** Suppress findings whose fingerprint is in this baseline file. */\n baseline?: string;\n /** Write current findings as a baseline to this file, then exit 0. */\n writeBaseline?: string;\n /** Suppress the human summary banner (still writes reports/output files). */\n quiet: boolean;\n /**\n * ANSI color policy for the human report (`--color` / `--no-color`). `\"auto\"`\n * (default) colors only a live terminal and honors `NO_COLOR` / `FORCE_COLOR`;\n * see {@link resolveColor}. Color is decoration only — never the sole signal.\n */\n colorChoice: ColorChoice;\n /** How many findings the human report lists (`--top N`). Default: 5. */\n topN?: number;\n /** Rule ids to suppress (from `quantakrypto.config.json` `disabledRules`). */\n disabledRules?: string[];\n /** Content-hash scan cache path (`--cache [path]`); reuse unchanged files. */\n cacheFile?: string;\n /** Run the BYOK LLM triage pass (`--triage`): annotate + re-sort, never suppress. */\n triage: boolean;\n /** Only triage findings at/above this seriousness (`--triage-floor`). Default: medium. */\n triageFloor?: Severity;\n /** Cap findings sent to the LLM during triage (`--max-findings`; spend guard). */\n maxFindings?: number;\n /** CNSA security tier for the migration-targets footer (`--tier`). Default: none. */\n tier?: SecurityTier;\n /**\n * Standards regime the migration guidance is tailored to (`--profile`): one of the\n * built-in ids (nist / cnsa-2.0 / bsi-tr-02102 / anssi / uk-ncsc). Default: none\n * (`--tier` maps to a profile for back-compat). Governs parameter sets, deadlines,\n * and the hybrid stance surfaced in remediation.\n */\n profile?: string;\n /** Org cryptography policy file (`--policy`) for the evidence report's §4 verdicts. */\n policy?: string;\n /**\n * External signer command for the evidence attestation (`--sign`). The report's\n * contentHash is piped to it on stdin; its stdout is recorded as the detached\n * signature. Only valid with `--format evidence`.\n */\n sign?: string;\n /** External RFC-3161 timestamper command for the evidence attestation (`--timestamp`). */\n timestamp?: string;\n /** How much source context leaves the machine (`--context`). Default: snippet. */\n contextLevel?: ContextLevel;\n /** Print the exact triage payload and exit without calling the provider (`--dry-run`). */\n dryRun: boolean;\n /** BYOK provider (`--llm-provider`). Default: anthropic. */\n llmProvider?: LlmProvider;\n /** BYOK model id (`--llm-model`). */\n llmModel?: string;\n /**\n * Omit code snippets from the JSON/SARIF report (`--no-snippets`). Passed to\n * core's reporters as `{ redactSnippets: true }`. Snippets of `sensitive`\n * findings are always omitted regardless of this flag.\n */\n noSnippets: boolean;\n /**\n * Explicit path to a `quantakrypto.config.json` (`--config <path>`). Overrides\n * auto-discovery at the scan root. Distinct from `--no-config`, which toggles\n * config/TLS *detector* scanning — this names the config FILE.\n */\n configFile?: string;\n /**\n * Disable `quantakrypto.config.json` auto-discovery (`--no-config-file`). Distinct\n * from `--no-config` (which skips config-file *detectors*).\n */\n noConfigFile: boolean;\n /**\n * Compute + emit HNDL (harvest-now-decrypt-later) exposure (`--hndl`). Reads\n * `hndl.yml` at the scan root, scores each finding + a repo summary, and adds\n * the exposure fields to the JSON / SARIF report. Additive: never changes the\n * exit code. See docs/HNDL.md.\n */\n hndl: boolean;\n /**\n * URL to a posture credential recorded in a crypto-agility manifest\n * (`--attestation`). Only consumed by `crypto-agility emit`; recorded verbatim,\n * never fetched (offline boundary). See docs/CRYPTO-AGILITY-MANIFEST.md.\n */\n attestation?: string;\n /**\n * Operator assertion of hybrid post-quantum key exchange for a crypto-agility\n * manifest (`--hybrid-kex` → true, `--no-hybrid-kex` → false). Left undefined,\n * the manifest reports `hybridKexInUse: null` (a static scan cannot observe a\n * negotiated TLS group). Only consumed by `crypto-agility emit`.\n */\n hybridKexInUse?: boolean;\n}\n\n/**\n * Option keys that a `quantakrypto.config.json` may also set. When such a key was set\n * by a CLI flag, the flag wins (precedence: flags > config > defaults); when it\n * was left at its default, config may fill it. {@link parseArgs} records which\n * of these keys came from an explicit flag in {@link ParsedRun.explicit}.\n */\nexport type ConfigurableKey =\n | \"severityThreshold\"\n | \"source\"\n | \"dependencies\"\n | \"config\"\n | \"include\"\n | \"ignore\"\n | \"maxFileSize\"\n | \"noDefaultIgnores\"\n | \"scanMinified\"\n | \"baseline\";\n\n/** A successful parse: resolved options plus which configurable keys were explicit. */\nexport interface ParsedRun {\n kind: \"run\";\n options: QscanOptions;\n /** The set of {@link ConfigurableKey}s the user set via a flag. */\n explicit: Set<ConfigurableKey>;\n}\n\n/** The `qscan hndl init` subcommand: scaffold an `hndl.yml`. */\nexport interface ParsedHndlInit {\n kind: \"hndl-init\";\n options: QscanOptions;\n}\n\n/**\n * Emit a crypto-agility manifest: either `qscan crypto-agility emit [path]` or the\n * `--crypto-agility` flag on a normal scan. Additive: it always exits 0, never\n * consulting the severity threshold.\n */\nexport interface ParsedCryptoAgilityEmit {\n kind: \"crypto-agility-emit\";\n options: QscanOptions;\n}\n\n/** Validate a local manifest file: `qscan crypto-agility validate <file>`. */\nexport interface ParsedCryptoAgilityValidate {\n kind: \"crypto-agility-validate\";\n /** Path to the manifest JSON file to check. */\n file: string;\n}\n\n/** Result of {@link parseArgs}: either resolved options or a meta action. */\nexport type ParsedArgs =\n | ParsedRun\n | ParsedHndlInit\n | ParsedCryptoAgilityEmit\n | ParsedCryptoAgilityValidate\n | { kind: \"help\" }\n | { kind: \"version\" };\n\n/** Thrown on malformed input; the CLI maps this to exit code 2. */\nexport class ArgError extends Error {\n override readonly name = \"ArgError\";\n}\n\n/** Default options, before any flags are applied. */\nexport function defaultOptions(): QscanOptions {\n return {\n path: \".\",\n format: \"human\",\n severityThreshold: \"high\",\n source: true,\n dependencies: true,\n config: true,\n ignore: [],\n include: [],\n noDefaultIgnores: false,\n scanMinified: false,\n changed: false,\n parallel: false,\n quiet: false,\n colorChoice: \"auto\",\n noSnippets: false,\n noConfigFile: false,\n triage: false,\n dryRun: false,\n hndl: false,\n mandates: [],\n failNow: false,\n };\n}\n\n/**\n * Parse a raw argv slice (i.e. without `node` and the script path).\n *\n * @throws {ArgError} On unknown flags, missing values, or invalid enum values.\n */\n/** Default scan-cache file when `--cache` is given without a path. */\nexport const DEFAULT_CACHE_FILE = \".quantakrypto-cache.json\";\n\nexport function parseArgs(argv: readonly string[]): ParsedArgs {\n // Subcommand dispatch: `qscan hndl <sub> …`. Only `init` exists today. `--help`\n // / `--version` anywhere still short-circuit to the meta actions (handled by\n // the shared loop below), so `qscan hndl init --help` shows help.\n if (argv[0] === \"hndl\") {\n const sub = argv[1];\n if (sub === undefined || sub.startsWith(\"-\")) {\n if (sub === \"--help\" || sub === \"-h\") return { kind: \"help\" };\n if (sub === \"--version\" || sub === \"-v\") return { kind: \"version\" };\n throw new ArgError(`\"hndl\" requires a subcommand (expected: init)`);\n }\n if (sub !== \"init\") {\n throw new ArgError(`unknown hndl subcommand \"${sub}\" (expected: init)`);\n }\n const parsed = parseRunArgs(argv.slice(2));\n if (parsed.kind !== \"run\") return parsed; // --help / --version passthrough\n return { kind: \"hndl-init\", options: parsed.options };\n }\n\n // Subcommand dispatch: `qscan crypto-agility <sub> …`.\n // emit [path] derive + write a manifest (always exits 0)\n // validate <file> check a local manifest against the schema\n if (argv[0] === \"crypto-agility\") {\n const sub = argv[1];\n if (sub === undefined || sub.startsWith(\"-\")) {\n if (sub === \"--help\" || sub === \"-h\") return { kind: \"help\" };\n if (sub === \"--version\" || sub === \"-v\") return { kind: \"version\" };\n throw new ArgError(`\"crypto-agility\" requires a subcommand (expected: emit, validate)`);\n }\n if (sub === \"emit\") {\n const parsed = parseRunArgs(argv.slice(2));\n // `--help`/`--version` short-circuit; a redundant `--crypto-agility` flag on\n // the emit subcommand already yields the emit kind (returned as-is).\n if (parsed.kind !== \"run\") return parsed;\n return { kind: \"crypto-agility-emit\", options: parsed.options };\n }\n if (sub === \"validate\") {\n const rest = argv.slice(2);\n if (rest.includes(\"--help\") || rest.includes(\"-h\")) return { kind: \"help\" };\n if (rest.includes(\"--version\") || rest.includes(\"-v\")) return { kind: \"version\" };\n const positionals = rest.filter((a) => !(a.startsWith(\"-\") && a !== \"-\"));\n if (positionals.length === 0) {\n throw new ArgError(`\"crypto-agility validate\" requires a manifest file path`);\n }\n if (positionals.length > 1) {\n throw new ArgError(\n `\"crypto-agility validate\" takes exactly one file (got ${positionals.length})`,\n );\n }\n return { kind: \"crypto-agility-validate\", file: positionals[0] as string };\n }\n throw new ArgError(`unknown crypto-agility subcommand \"${sub}\" (expected: emit, validate)`);\n }\n return parseRunArgs(argv);\n}\n\n/** Parse the run-mode grammar (flags + a single optional positional path). */\nfunction parseRunArgs(argv: readonly string[]): ParsedArgs {\n const options = defaultOptions();\n const explicit = new Set<ConfigurableKey>();\n let positional: string | undefined;\n // Set by `--crypto-agility`: the whole run becomes a manifest emit (exit 0).\n let emitManifest = false;\n\n // Manual index walk so flags can consume the following token as a value.\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i] as string;\n\n // `--flag=value` → split into flag + inline value.\n let inlineValue: string | undefined;\n let flag = arg;\n if (arg.startsWith(\"--\") && arg.includes(\"=\")) {\n const eq = arg.indexOf(\"=\");\n flag = arg.slice(0, eq);\n inlineValue = arg.slice(eq + 1);\n }\n\n /** Consume a value for `flag`: prefer the inline `=value`, else the next token. */\n const takeValue = (): string => {\n if (inlineValue !== undefined) return inlineValue;\n const next = argv[i + 1];\n if (next === undefined || (next.startsWith(\"-\") && next !== \"-\")) {\n throw new ArgError(`option \"${flag}\" requires a value`);\n }\n i++;\n return next;\n };\n\n /**\n * Reject an inline `=value` on a boolean flag. Without this, `--quiet=false`\n * would silently ignore the value and turn the flag ON — the opposite of the\n * caller's intent. Boolean flags take no value, so any `=value` is an error.\n */\n const rejectInlineValue = (): void => {\n if (inlineValue !== undefined) {\n throw new ArgError(`option \"${flag}\" is a boolean flag and takes no value`);\n }\n };\n\n switch (flag) {\n case \"-h\":\n case \"--help\":\n return { kind: \"help\" };\n case \"-v\":\n case \"--version\":\n return { kind: \"version\" };\n\n case \"--format\":\n options.format = asFormat(takeValue());\n break;\n case \"--cbom\":\n rejectInlineValue();\n options.format = \"cbom\";\n break;\n case \"--merge\":\n // Merge an external CBOM (e.g. a qprobe endpoint CBOM) into the --cbom\n // output via CycloneDX bom-link. Repeatable.\n (options.mergeCboms ??= []).push(takeValue());\n break;\n case \"-o\":\n case \"--output\":\n options.output = takeValue();\n break;\n case \"--severity-threshold\":\n options.severityThreshold = asSeverity(takeValue());\n explicit.add(\"severityThreshold\");\n break;\n\n case \"--no-source\":\n rejectInlineValue();\n options.source = false;\n explicit.add(\"source\");\n break;\n case \"--no-deps\":\n rejectInlineValue();\n options.dependencies = false;\n explicit.add(\"dependencies\");\n break;\n case \"--no-config\":\n rejectInlineValue();\n options.config = false;\n explicit.add(\"config\");\n break;\n\n case \"--ignore\":\n options.ignore.push(takeValue());\n explicit.add(\"ignore\");\n break;\n case \"--include\":\n options.include.push(takeValue());\n explicit.add(\"include\");\n break;\n case \"--max-file-size\":\n options.maxFileSize = asInt(takeValue(), \"--max-file-size\");\n explicit.add(\"maxFileSize\");\n break;\n case \"--top\":\n options.topN = asInt(takeValue(), \"--top\");\n break;\n case \"--triage\":\n rejectInlineValue();\n options.triage = true;\n break;\n case \"--triage-floor\":\n options.triageFloor = asSeverity(takeValue());\n break;\n case \"--max-findings\":\n options.maxFindings = asInt(takeValue(), \"--max-findings\");\n break;\n case \"--tier\":\n options.tier = asTier(takeValue());\n break;\n case \"--profile\":\n options.profile = asProfile(takeValue());\n break;\n case \"--mandate\":\n options.mandates.push(takeValue());\n break;\n case \"--lead-months\":\n options.leadMonths = asInt(takeValue(), \"--lead-months\");\n break;\n case \"--fail-now\":\n rejectInlineValue();\n options.failNow = true;\n break;\n case \"--context\":\n options.contextLevel = asContextLevel(takeValue());\n break;\n case \"--dry-run\":\n rejectInlineValue();\n options.dryRun = true;\n break;\n case \"--llm-provider\":\n options.llmProvider = asProvider(takeValue());\n break;\n case \"--llm-model\":\n options.llmModel = takeValue();\n break;\n case \"--cache\": {\n // `--cache` alone uses the default file; `--cache <path>` names it.\n if (inlineValue !== undefined) {\n options.cacheFile = inlineValue;\n } else {\n const next = argv[i + 1];\n if (next !== undefined && !(next.startsWith(\"-\") && next !== \"-\")) {\n options.cacheFile = next;\n i++;\n } else {\n options.cacheFile = DEFAULT_CACHE_FILE;\n }\n }\n break;\n }\n case \"--no-default-ignores\":\n rejectInlineValue();\n options.noDefaultIgnores = true;\n explicit.add(\"noDefaultIgnores\");\n break;\n case \"--scan-minified\":\n rejectInlineValue();\n options.scanMinified = true;\n explicit.add(\"scanMinified\");\n break;\n\n // `quantakrypto.config.json` FILE controls (distinct from `--no-config`, which\n // toggles config/TLS *detector* scanning above).\n case \"--config\":\n options.configFile = takeValue();\n break;\n case \"--no-config-file\":\n rejectInlineValue();\n options.noConfigFile = true;\n break;\n\n case \"--changed\":\n rejectInlineValue();\n options.changed = true;\n break;\n case \"--since\":\n options.since = takeValue();\n options.changed = true; // --since implies incremental mode\n break;\n\n case \"--parallel\":\n rejectInlineValue();\n options.parallel = true;\n break;\n case \"--concurrency\": {\n const n = asInt(takeValue(), \"--concurrency\");\n options.concurrency = n;\n // `--concurrency 0` documents \"serial\": core treats <1 as \"auto\" (full\n // parallelism), so without this special-case 0 would do the OPPOSITE of\n // what's documented. 0 forces the in-process serial path; any value >= 1\n // implies parallel.\n options.parallel = n >= 1;\n break;\n }\n\n case \"--baseline\":\n options.baseline = takeValue();\n explicit.add(\"baseline\");\n break;\n case \"--policy\":\n options.policy = takeValue();\n break;\n case \"--sign\":\n options.sign = takeValue();\n break;\n case \"--timestamp\":\n options.timestamp = takeValue();\n break;\n case \"--write-baseline\":\n options.writeBaseline = takeValue();\n break;\n case \"--quiet\":\n rejectInlineValue();\n options.quiet = true;\n break;\n case \"--color\":\n rejectInlineValue();\n options.colorChoice = \"always\";\n break;\n case \"--no-color\":\n rejectInlineValue();\n options.colorChoice = \"never\";\n break;\n case \"--no-snippets\":\n rejectInlineValue();\n options.noSnippets = true;\n break;\n case \"--hndl\":\n rejectInlineValue();\n options.hndl = true;\n break;\n\n // Crypto-agility manifest controls (see `crypto-agility emit`).\n case \"--crypto-agility\":\n rejectInlineValue();\n emitManifest = true;\n break;\n case \"--attestation\":\n options.attestation = takeValue();\n break;\n case \"--hybrid-kex\":\n rejectInlineValue();\n options.hybridKexInUse = true;\n break;\n case \"--no-hybrid-kex\":\n rejectInlineValue();\n options.hybridKexInUse = false;\n break;\n\n default:\n if (flag.startsWith(\"-\") && flag !== \"-\") {\n throw new ArgError(`unknown option \"${flag}\"`);\n }\n if (positional !== undefined) {\n throw new ArgError(\n `unexpected extra argument \"${arg}\" (path already set to \"${positional}\")`,\n );\n }\n positional = arg;\n break;\n }\n }\n\n if (positional !== undefined) options.path = positional;\n // `--crypto-agility` turns the whole run into a manifest emit, taking it off the\n // findings-threshold exit path entirely (the emit always exits 0).\n if (emitManifest) return { kind: \"crypto-agility-emit\", options };\n return { kind: \"run\", options, explicit };\n}\n\n/** Validate/normalize a `--format` value. */\nexport function asFormat(value: string): QscanFormat {\n if ((FORMATS as readonly string[]).includes(value)) return value as QscanFormat;\n throw new ArgError(`invalid --format \"${value}\" (expected one of: ${FORMATS.join(\", \")})`);\n}\n\n/** Validate/normalize a `--context` level. */\nfunction asContextLevel(value: string): ContextLevel {\n if ((CONTEXT_LEVELS as readonly string[]).includes(value)) return value as ContextLevel;\n throw new ArgError(\n `invalid --context \"${value}\" (expected one of: ${CONTEXT_LEVELS.join(\", \")})`,\n );\n}\n\n/** Validate/normalize a `--tier` value. */\nfunction asTier(value: string): SecurityTier {\n if ((SECURITY_TIERS as readonly string[]).includes(value)) return value as SecurityTier;\n throw new ArgError(`invalid --tier \"${value}\" (expected one of: ${SECURITY_TIERS.join(\", \")})`);\n}\n\n/** Validate a `--profile` value against the built-in standards regimes. */\nfunction asProfile(value: string): string {\n const ids = standardsProfileIds();\n if (ids.includes(value)) return value;\n throw new ArgError(`invalid --profile \"${value}\" (expected one of: ${ids.join(\", \")})`);\n}\n\n/** Validate/normalize a `--llm-provider` value. */\nfunction asProvider(value: string): LlmProvider {\n if ((LLM_PROVIDERS as readonly string[]).includes(value)) return value as LlmProvider;\n throw new ArgError(\n `invalid --llm-provider \"${value}\" (expected one of: ${LLM_PROVIDERS.join(\", \")})`,\n );\n}\n\n/** Validate/normalize a non-negative integer flag value. */\nexport function asInt(value: string, flag: string): number {\n if (!/^\\d+$/.test(value)) {\n throw new ArgError(`invalid ${flag} \"${value}\" (expected a non-negative integer)`);\n }\n return Number.parseInt(value, 10);\n}\n\n/** Validate/normalize a severity value. */\nexport function asSeverity(value: string): Severity {\n if ((SEVERITY_ORDER as readonly string[]).includes(value)) return value as Severity;\n throw new ArgError(`invalid severity \"${value}\" (expected one of: ${SEVERITY_ORDER.join(\", \")})`);\n}\n"]}
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG;AAiBH,2EAA2E;AAC3E,wBAAsB,IAAI,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA6GnE"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG;AAuBH,2EAA2E;AAC3E,wBAAsB,IAAI,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAmMnE"}
package/dist/cli.js CHANGED
@@ -17,7 +17,7 @@ import { ArgError, parseArgs } from "./args.js";
17
17
  import { resolveColor } from "./color.js";
18
18
  import { resolveConfig } from "./config.js";
19
19
  import { HELP_TEXT, versionLine } from "./help.js";
20
- import { EXIT, runQscan } from "./index.js";
20
+ import { EXIT, runCryptoAgilityEmit, runCryptoAgilityValidate, runHndlInit, runQscan, } from "./index.js";
21
21
  /** Run the CLI and return the desired process exit code (never throws). */
22
22
  export async function main(argv) {
23
23
  let parsed;
@@ -40,6 +40,85 @@ export async function main(argv) {
40
40
  process.stdout.write(`${versionLine()}\n`);
41
41
  return EXIT.OK;
42
42
  }
43
+ // `qscan hndl init`: scaffold an hndl.yml from a seeding scan. Refuses to
44
+ // overwrite an existing file.
45
+ if (parsed.kind === "hndl-init") {
46
+ try {
47
+ const init = await runHndlInit(parsed.options);
48
+ if (init.exists) {
49
+ process.stderr.write(`qscan: ${init.path} already exists; refusing to overwrite. Edit it, or remove it and re-run.\n`);
50
+ return EXIT.ERROR;
51
+ }
52
+ await writeFile(init.path, init.content, "utf8");
53
+ process.stderr.write(`qscan: wrote ${init.path} (seeded from ${init.seededFindings} data-adjacent finding(s) of ${init.findingsScanned} scanned).\n`);
54
+ process.stderr.write(`qscan: review the assets, then run "qscan ${parsed.options.path} --hndl" for exposure scores.\n`);
55
+ return EXIT.OK;
56
+ }
57
+ catch (err) {
58
+ if (isErrno(err) && err.code === "ENOENT") {
59
+ process.stderr.write(`qscan: path not found: ${parsed.options.path}\n`);
60
+ return EXIT.ERROR;
61
+ }
62
+ const message = err instanceof Error ? err.message : String(err);
63
+ process.stderr.write(`qscan: ${message}\n`);
64
+ return EXIT.ERROR;
65
+ }
66
+ }
67
+ // `qscan crypto-agility emit` / `--crypto-agility`: derive a posture manifest and
68
+ // write it to stdout (or `--output`). Additive: always exits 0, never consulting
69
+ // the severity threshold.
70
+ if (parsed.kind === "crypto-agility-emit") {
71
+ try {
72
+ const { manifest } = await runCryptoAgilityEmit(parsed.options);
73
+ const out = manifest.endsWith("\n") ? manifest : `${manifest}\n`;
74
+ if (parsed.options.output) {
75
+ await writeFile(parsed.options.output, out, "utf8");
76
+ if (!parsed.options.quiet) {
77
+ process.stderr.write(`qscan: wrote crypto-agility manifest to ${parsed.options.output}\n`);
78
+ }
79
+ }
80
+ else {
81
+ await writeStdout(out);
82
+ }
83
+ return EXIT.OK;
84
+ }
85
+ catch (err) {
86
+ if (isErrno(err) && err.code === "ENOENT") {
87
+ const missing = typeof err.path === "string" && err.path ? err.path : parsed.options.path;
88
+ process.stderr.write(`qscan: path not found: ${missing}\n`);
89
+ return EXIT.ERROR;
90
+ }
91
+ const message = err instanceof Error ? err.message : String(err);
92
+ process.stderr.write(`qscan: ${message}\n`);
93
+ return EXIT.ERROR;
94
+ }
95
+ }
96
+ // `qscan crypto-agility validate <file>`: check a LOCAL manifest against the
97
+ // schema. Exit 0 when valid, 1 when the manifest is invalid, 2 on an I/O error.
98
+ if (parsed.kind === "crypto-agility-validate") {
99
+ let validation;
100
+ try {
101
+ validation = await runCryptoAgilityValidate(parsed.file);
102
+ }
103
+ catch (err) {
104
+ if (isErrno(err) && err.code === "ENOENT") {
105
+ process.stderr.write(`qscan: path not found: ${parsed.file}\n`);
106
+ return EXIT.ERROR;
107
+ }
108
+ const message = err instanceof Error ? err.message : String(err);
109
+ process.stderr.write(`qscan: ${message}\n`);
110
+ return EXIT.ERROR;
111
+ }
112
+ if (validation.valid) {
113
+ process.stdout.write(`qscan: ${parsed.file} is a valid crypto-agility manifest\n`);
114
+ return EXIT.OK;
115
+ }
116
+ process.stderr.write(`qscan: ${parsed.file} is not a valid crypto-agility manifest:\n`);
117
+ for (const e of validation.errors) {
118
+ process.stderr.write(` - ${e}\n`);
119
+ }
120
+ return EXIT.FINDINGS;
121
+ }
43
122
  // Resolve `quantakrypto.config.json` (flags > config > defaults) before scanning.
44
123
  let options;
45
124
  try {
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n/**\n * qScan command-line entry point.\n *\n * Thin shell over the programmatic API in `./index.ts`:\n * parse argv → runQscan → print/write report → process.exit(code).\n *\n * All policy (scanning, baseline, thresholds, rendering) lives in `index.ts`;\n * this file only deals with argv, stdout/stderr, files, and exit codes.\n */\n\nimport { realpathSync } from \"node:fs\";\nimport { writeFile } from \"node:fs/promises\";\nimport process from \"node:process\";\nimport { fileURLToPath } from \"node:url\";\n\nimport { ConfigError } from \"@quantakrypto/core\";\n\nimport { ArgError, parseArgs } from \"./args.js\";\nimport type { ParsedArgs, QscanOptions } from \"./args.js\";\nimport { resolveColor } from \"./color.js\";\nimport { resolveConfig } from \"./config.js\";\nimport { HELP_TEXT, versionLine } from \"./help.js\";\nimport { EXIT, runQscan } from \"./index.js\";\nimport type { QscanRun } from \"./index.js\";\n\n/** Run the CLI and return the desired process exit code (never throws). */\nexport async function main(argv: readonly string[]): Promise<number> {\n let parsed: ParsedArgs;\n try {\n parsed = parseArgs(argv);\n } catch (err) {\n if (err instanceof ArgError) {\n process.stderr.write(`qscan: ${err.message}\\n`);\n process.stderr.write(`Run \"qscan --help\" for usage.\\n`);\n return EXIT.ERROR;\n }\n throw err;\n }\n\n if (parsed.kind === \"help\") {\n process.stdout.write(HELP_TEXT);\n return EXIT.OK;\n }\n if (parsed.kind === \"version\") {\n process.stdout.write(`${versionLine()}\\n`);\n return EXIT.OK;\n }\n\n // Resolve `quantakrypto.config.json` (flags > config > defaults) before scanning.\n let options: QscanOptions;\n try {\n const resolved = await resolveConfig(parsed.options, parsed.explicit);\n options = resolved.options;\n if (!options.quiet) {\n for (const w of resolved.warnings) {\n process.stderr.write(`qscan: config warning: ${w}\\n`);\n }\n if (resolved.configPath) {\n process.stderr.write(`qscan: using config ${resolved.configPath}\\n`);\n }\n }\n } catch (err) {\n if (err instanceof ConfigError) {\n process.stderr.write(`qscan: ${err.message}\\n`);\n return EXIT.ERROR;\n }\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`qscan: ${message}\\n`);\n return EXIT.ERROR;\n }\n\n // Color policy: --color/--no-color > NO_COLOR/FORCE_COLOR > interactive stdout.\n // Color is decoration only (every signal is also text), so this is purely an\n // accessibility / pipe-safety control. See resolveColor for the precedence.\n const color = resolveColor({\n choice: options.colorChoice,\n format: options.format,\n toFile: Boolean(options.output),\n isTTY: Boolean(process.stdout.isTTY),\n env: { NO_COLOR: process.env.NO_COLOR, FORCE_COLOR: process.env.FORCE_COLOR },\n });\n\n let run: QscanRun;\n try {\n run = await runQscan(options, { color });\n } catch (err) {\n // A missing file is the most common failure; a raw \"ENOENT: no such file or\n // directory, stat '…'\" reads like a tool bug. Turn it into a plain, actionable\n // line (still exit 2). Report the ACTUAL missing path from `err.path` when present\n // — an ENOENT can come from `--policy`, `--baseline`, or `--write-baseline`, not\n // just the scan path, and blaming the (existing) scan path misdirects the user.\n if (isErrno(err) && err.code === \"ENOENT\") {\n const missing = typeof err.path === \"string\" && err.path ? err.path : options.path;\n process.stderr.write(`qscan: path not found: ${missing}\\n`);\n return EXIT.ERROR;\n }\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`qscan: ${message}\\n`);\n return EXIT.ERROR;\n }\n\n // --write-baseline: report what was written and stop.\n if (run.baselineWritten) {\n if (!options.quiet) {\n const n = run.baselineWritten.fingerprints.length;\n process.stderr.write(\n `qscan: wrote baseline with ${n} fingerprint${n === 1 ? \"\" : \"s\"} to ${options.writeBaseline}\\n`,\n );\n }\n return run.exitCode;\n }\n\n const report = run.report ?? \"\";\n try {\n if (options.output) {\n await writeFile(options.output, report.endsWith(\"\\n\") ? report : `${report}\\n`, \"utf8\");\n if (!options.quiet) {\n process.stderr.write(`qscan: wrote ${options.format} report to ${options.output}\\n`);\n }\n } else if (!options.quiet || options.format !== \"human\") {\n // In quiet mode we still emit machine formats to stdout (the point of a\n // pipe), but suppress the human banner.\n await writeStdout(report.endsWith(\"\\n\") ? report : `${report}\\n`);\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`qscan: ${message}\\n`);\n return EXIT.ERROR;\n }\n\n if (!options.quiet && run.suppressed.length > 0) {\n process.stderr.write(`qscan: suppressed ${run.suppressed.length} finding(s) via baseline\\n`);\n }\n\n return run.exitCode;\n}\n\n/** True for Node system errors (fs/os), which carry a string `code` like `ENOENT`. */\nfunction isErrno(err: unknown): err is NodeJS.ErrnoException {\n return err instanceof Error && typeof (err as NodeJS.ErrnoException).code === \"string\";\n}\n\n/**\n * Write to stdout, awaiting `drain` when the kernel buffer is full.\n *\n * `process.stdout.write` returns `false` when the OS buffer can't accept the\n * whole chunk (typical for large reports down a pipe or file redirect). If the\n * process then exits before the buffer flushes, the tail of the report is lost.\n * When the write is not fully flushed we wait for the `drain` event, so the\n * bytes are handed to the OS before we return and the report is never\n * truncated, regardless of how the caller exits. A fully-flushed write (the\n * common case, and a TTY) resolves synchronously on the next microtask.\n */\nfunction writeStdout(chunk: string): Promise<void> {\n const flushed = process.stdout.write(chunk);\n if (flushed) return Promise.resolve();\n return new Promise((resolve) => process.stdout.once(\"drain\", resolve));\n}\n\n/**\n * True when this module is the program's entry point. Resolves symlinks so the\n * check also holds when launched via the `qscan` bin shim in node_modules/.bin\n * (npm symlinks it) or through /tmp -> /private/tmp on macOS — otherwise\n * `npx @quantakrypto/qscan` (and `npm i -g`) would be a silent no-op.\n */\nfunction isMainModule(): boolean {\n const argv1 = process.argv[1];\n if (argv1 === undefined) return false;\n const thisPath = fileURLToPath(import.meta.url);\n try {\n return realpathSync(argv1) === realpathSync(thisPath);\n } catch {\n return argv1 === thisPath;\n }\n}\n\n// Only auto-run when invoked as a script (not when imported by a test).\nconst invokedDirectly = isMainModule();\n\nif (invokedDirectly) {\n main(process.argv.slice(2))\n .then((code) => {\n // Set the exit code and return WITHOUT calling process.exit(): a bare\n // process.exit() tears down the event loop before stdout's async buffer\n // drains, truncating large SARIF/JSON reports written to a pipe or file\n // redirect. Letting the loop empty naturally lets the buffer flush first.\n process.exitCode = code;\n })\n .catch((err) => {\n const message = err instanceof Error ? (err.stack ?? err.message) : String(err);\n process.stderr.write(`qscan: fatal: ${message}\\n`);\n process.exitCode = EXIT.ERROR;\n });\n}\n"]}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n/**\n * qScan command-line entry point.\n *\n * Thin shell over the programmatic API in `./index.ts`:\n * parse argv → runQscan → print/write report → process.exit(code).\n *\n * All policy (scanning, baseline, thresholds, rendering) lives in `index.ts`;\n * this file only deals with argv, stdout/stderr, files, and exit codes.\n */\n\nimport { realpathSync } from \"node:fs\";\nimport { writeFile } from \"node:fs/promises\";\nimport process from \"node:process\";\nimport { fileURLToPath } from \"node:url\";\n\nimport { ConfigError } from \"@quantakrypto/core\";\n\nimport { ArgError, parseArgs } from \"./args.js\";\nimport type { ParsedArgs, QscanOptions } from \"./args.js\";\nimport { resolveColor } from \"./color.js\";\nimport { resolveConfig } from \"./config.js\";\nimport { HELP_TEXT, versionLine } from \"./help.js\";\nimport {\n EXIT,\n runCryptoAgilityEmit,\n runCryptoAgilityValidate,\n runHndlInit,\n runQscan,\n} from \"./index.js\";\nimport type { QscanRun } from \"./index.js\";\n\n/** Run the CLI and return the desired process exit code (never throws). */\nexport async function main(argv: readonly string[]): Promise<number> {\n let parsed: ParsedArgs;\n try {\n parsed = parseArgs(argv);\n } catch (err) {\n if (err instanceof ArgError) {\n process.stderr.write(`qscan: ${err.message}\\n`);\n process.stderr.write(`Run \"qscan --help\" for usage.\\n`);\n return EXIT.ERROR;\n }\n throw err;\n }\n\n if (parsed.kind === \"help\") {\n process.stdout.write(HELP_TEXT);\n return EXIT.OK;\n }\n if (parsed.kind === \"version\") {\n process.stdout.write(`${versionLine()}\\n`);\n return EXIT.OK;\n }\n\n // `qscan hndl init`: scaffold an hndl.yml from a seeding scan. Refuses to\n // overwrite an existing file.\n if (parsed.kind === \"hndl-init\") {\n try {\n const init = await runHndlInit(parsed.options);\n if (init.exists) {\n process.stderr.write(\n `qscan: ${init.path} already exists; refusing to overwrite. Edit it, or remove it and re-run.\\n`,\n );\n return EXIT.ERROR;\n }\n await writeFile(init.path, init.content, \"utf8\");\n process.stderr.write(\n `qscan: wrote ${init.path} (seeded from ${init.seededFindings} data-adjacent finding(s) of ${init.findingsScanned} scanned).\\n`,\n );\n process.stderr.write(\n `qscan: review the assets, then run \"qscan ${parsed.options.path} --hndl\" for exposure scores.\\n`,\n );\n return EXIT.OK;\n } catch (err) {\n if (isErrno(err) && err.code === \"ENOENT\") {\n process.stderr.write(`qscan: path not found: ${parsed.options.path}\\n`);\n return EXIT.ERROR;\n }\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`qscan: ${message}\\n`);\n return EXIT.ERROR;\n }\n }\n\n // `qscan crypto-agility emit` / `--crypto-agility`: derive a posture manifest and\n // write it to stdout (or `--output`). Additive: always exits 0, never consulting\n // the severity threshold.\n if (parsed.kind === \"crypto-agility-emit\") {\n try {\n const { manifest } = await runCryptoAgilityEmit(parsed.options);\n const out = manifest.endsWith(\"\\n\") ? manifest : `${manifest}\\n`;\n if (parsed.options.output) {\n await writeFile(parsed.options.output, out, \"utf8\");\n if (!parsed.options.quiet) {\n process.stderr.write(\n `qscan: wrote crypto-agility manifest to ${parsed.options.output}\\n`,\n );\n }\n } else {\n await writeStdout(out);\n }\n return EXIT.OK;\n } catch (err) {\n if (isErrno(err) && err.code === \"ENOENT\") {\n const missing = typeof err.path === \"string\" && err.path ? err.path : parsed.options.path;\n process.stderr.write(`qscan: path not found: ${missing}\\n`);\n return EXIT.ERROR;\n }\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`qscan: ${message}\\n`);\n return EXIT.ERROR;\n }\n }\n\n // `qscan crypto-agility validate <file>`: check a LOCAL manifest against the\n // schema. Exit 0 when valid, 1 when the manifest is invalid, 2 on an I/O error.\n if (parsed.kind === \"crypto-agility-validate\") {\n let validation;\n try {\n validation = await runCryptoAgilityValidate(parsed.file);\n } catch (err) {\n if (isErrno(err) && err.code === \"ENOENT\") {\n process.stderr.write(`qscan: path not found: ${parsed.file}\\n`);\n return EXIT.ERROR;\n }\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`qscan: ${message}\\n`);\n return EXIT.ERROR;\n }\n if (validation.valid) {\n process.stdout.write(`qscan: ${parsed.file} is a valid crypto-agility manifest\\n`);\n return EXIT.OK;\n }\n process.stderr.write(`qscan: ${parsed.file} is not a valid crypto-agility manifest:\\n`);\n for (const e of validation.errors) {\n process.stderr.write(` - ${e}\\n`);\n }\n return EXIT.FINDINGS;\n }\n\n // Resolve `quantakrypto.config.json` (flags > config > defaults) before scanning.\n let options: QscanOptions;\n try {\n const resolved = await resolveConfig(parsed.options, parsed.explicit);\n options = resolved.options;\n if (!options.quiet) {\n for (const w of resolved.warnings) {\n process.stderr.write(`qscan: config warning: ${w}\\n`);\n }\n if (resolved.configPath) {\n process.stderr.write(`qscan: using config ${resolved.configPath}\\n`);\n }\n }\n } catch (err) {\n if (err instanceof ConfigError) {\n process.stderr.write(`qscan: ${err.message}\\n`);\n return EXIT.ERROR;\n }\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`qscan: ${message}\\n`);\n return EXIT.ERROR;\n }\n\n // Color policy: --color/--no-color > NO_COLOR/FORCE_COLOR > interactive stdout.\n // Color is decoration only (every signal is also text), so this is purely an\n // accessibility / pipe-safety control. See resolveColor for the precedence.\n const color = resolveColor({\n choice: options.colorChoice,\n format: options.format,\n toFile: Boolean(options.output),\n isTTY: Boolean(process.stdout.isTTY),\n env: { NO_COLOR: process.env.NO_COLOR, FORCE_COLOR: process.env.FORCE_COLOR },\n });\n\n let run: QscanRun;\n try {\n run = await runQscan(options, { color });\n } catch (err) {\n // A missing file is the most common failure; a raw \"ENOENT: no such file or\n // directory, stat '…'\" reads like a tool bug. Turn it into a plain, actionable\n // line (still exit 2). Report the ACTUAL missing path from `err.path` when present\n // — an ENOENT can come from `--policy`, `--baseline`, or `--write-baseline`, not\n // just the scan path, and blaming the (existing) scan path misdirects the user.\n if (isErrno(err) && err.code === \"ENOENT\") {\n const missing = typeof err.path === \"string\" && err.path ? err.path : options.path;\n process.stderr.write(`qscan: path not found: ${missing}\\n`);\n return EXIT.ERROR;\n }\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`qscan: ${message}\\n`);\n return EXIT.ERROR;\n }\n\n // --write-baseline: report what was written and stop.\n if (run.baselineWritten) {\n if (!options.quiet) {\n const n = run.baselineWritten.fingerprints.length;\n process.stderr.write(\n `qscan: wrote baseline with ${n} fingerprint${n === 1 ? \"\" : \"s\"} to ${options.writeBaseline}\\n`,\n );\n }\n return run.exitCode;\n }\n\n const report = run.report ?? \"\";\n try {\n if (options.output) {\n await writeFile(options.output, report.endsWith(\"\\n\") ? report : `${report}\\n`, \"utf8\");\n if (!options.quiet) {\n process.stderr.write(`qscan: wrote ${options.format} report to ${options.output}\\n`);\n }\n } else if (!options.quiet || options.format !== \"human\") {\n // In quiet mode we still emit machine formats to stdout (the point of a\n // pipe), but suppress the human banner.\n await writeStdout(report.endsWith(\"\\n\") ? report : `${report}\\n`);\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`qscan: ${message}\\n`);\n return EXIT.ERROR;\n }\n\n if (!options.quiet && run.suppressed.length > 0) {\n process.stderr.write(`qscan: suppressed ${run.suppressed.length} finding(s) via baseline\\n`);\n }\n\n return run.exitCode;\n}\n\n/** True for Node system errors (fs/os), which carry a string `code` like `ENOENT`. */\nfunction isErrno(err: unknown): err is NodeJS.ErrnoException {\n return err instanceof Error && typeof (err as NodeJS.ErrnoException).code === \"string\";\n}\n\n/**\n * Write to stdout, awaiting `drain` when the kernel buffer is full.\n *\n * `process.stdout.write` returns `false` when the OS buffer can't accept the\n * whole chunk (typical for large reports down a pipe or file redirect). If the\n * process then exits before the buffer flushes, the tail of the report is lost.\n * When the write is not fully flushed we wait for the `drain` event, so the\n * bytes are handed to the OS before we return and the report is never\n * truncated, regardless of how the caller exits. A fully-flushed write (the\n * common case, and a TTY) resolves synchronously on the next microtask.\n */\nfunction writeStdout(chunk: string): Promise<void> {\n const flushed = process.stdout.write(chunk);\n if (flushed) return Promise.resolve();\n return new Promise((resolve) => process.stdout.once(\"drain\", resolve));\n}\n\n/**\n * True when this module is the program's entry point. Resolves symlinks so the\n * check also holds when launched via the `qscan` bin shim in node_modules/.bin\n * (npm symlinks it) or through /tmp -> /private/tmp on macOS — otherwise\n * `npx @quantakrypto/qscan` (and `npm i -g`) would be a silent no-op.\n */\nfunction isMainModule(): boolean {\n const argv1 = process.argv[1];\n if (argv1 === undefined) return false;\n const thisPath = fileURLToPath(import.meta.url);\n try {\n return realpathSync(argv1) === realpathSync(thisPath);\n } catch {\n return argv1 === thisPath;\n }\n}\n\n// Only auto-run when invoked as a script (not when imported by a test).\nconst invokedDirectly = isMainModule();\n\nif (invokedDirectly) {\n main(process.argv.slice(2))\n .then((code) => {\n // Set the exit code and return WITHOUT calling process.exit(): a bare\n // process.exit() tears down the event loop before stdout's async buffer\n // drains, truncating large SARIF/JSON reports written to a pipe or file\n // redirect. Letting the loop empty naturally lets the buffer flush first.\n process.exitCode = code;\n })\n .catch((err) => {\n const message = err instanceof Error ? (err.stack ?? err.message) : String(err);\n process.stderr.write(`qscan: fatal: ${message}\\n`);\n process.exitCode = EXIT.ERROR;\n });\n}\n"]}
package/dist/help.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * filesystem or process side effects.
6
6
  */
7
7
  /** The full `--help` screen. */
8
- export declare const HELP_TEXT = "qscan \u2014 find quantum-vulnerable cryptography in any codebase\n\nUSAGE\n qscan [path] [options]\n\nARGUMENTS\n path Directory or file to scan (default: \".\")\n\nOPTIONS\n --format <human|json|sarif|cbom|evidence|vex>\n Output format (default: human)\n --cbom Alias for --format cbom (CycloneDX CBOM)\n --format vex OpenVEX 0.2.0 document \u2014 one statement per rule,\n status \"affected\", with remediation + any\n --triage verdict for supply-chain VEX pipelines\n --merge <cbom.json> Merge an external CBOM (e.g. a qprobe endpoint\n CBOM) into the --cbom output via CycloneDX\n bom-link \u2014 one combined code + infra CBOM.\n Repeatable.\n --format evidence ISO 27001 A.8.24 readiness report \u2014 findings +\n inventory + CBOM + a deterministic content hash\n (sign/timestamp it with an external signer)\n --policy <file> Crypto-policy JSON; adds conformant/violation/\n transition verdicts to the evidence report\n --sign <command> Sign the evidence report: its contentHash is\n piped to <command> on stdin; stdout is recorded\n as the detached signature (needs --format evidence)\n --timestamp <command> Like --sign, but records an RFC-3161 timestamp\n token from <command> (needs --format evidence)\n -o, --output <file> Write the report to a file instead of stdout\n --severity-threshold <level> Fail (exit 1) on findings at/above this level;\n one of critical|high|medium|low|info\n (default: high)\n --no-source Skip scanning source files for inline crypto\n --no-deps Skip scanning dependency manifests\n --no-config Skip scanning config files (TLS/certificates)\n --config <path> Use this quantakrypto.config.json instead of\n auto-discovering one at the scan root\n --no-config-file Disable quantakrypto.config.json auto-discovery\n --ignore <pattern> Exclude paths matching <pattern> (repeatable)\n --include <pattern> Restrict the scan to matching paths (repeatable)\n --max-file-size <bytes> Skip files larger than <bytes> (default: 2 MiB)\n --no-default-ignores Don't skip node_modules/.git/dist by default\n --scan-minified Scan minified/generated/bundled files too\n --changed Scan only files changed in the git work tree\n --since <git-ref> With --changed, diff against <git-ref>\n --parallel Scan using a worker-thread pool when worthwhile\n --concurrency <n> Worker count for --parallel (implies --parallel);\n 0 forces the in-process serial path\n --top <n> List <n> findings in the human report (default: 5)\n --tier <category-3|category-5> Add CNSA migration targets to the report footer\n (category-5 = CNSA 2.0: ML-KEM-1024 / ML-DSA-87;\n an alias for --profile cnsa-2.0)\n --profile <id> Tailor migration guidance to a standards regime:\n nist (default) | cnsa-2.0 | bsi-tr-02102 | anssi |\n uk-ncsc. Sets the parameter sets, deadlines, and\n whether hybridization is required/recommended/optional\n --cache [file] Reuse findings for unchanged files across runs\n (default file: .quantakrypto-cache.json)\n --triage BYOK LLM pass that re-ranks findings by real\n exposure and explains them (never suppresses,\n never changes the exit code). Needs an API key in\n QK_LLM_API_KEY / ANTHROPIC_API_KEY / OPENAI_API_KEY\n --triage-floor <level> Only triage findings at/above this level (default: medium)\n --max-findings <n> Cap findings sent to the LLM during triage (default: 100; spend guard)\n --context <level> Source shared with the LLM: metadata|snippet|\n function|file (default: snippet; secrets always redacted)\n --dry-run With --triage, print the exact payload that would\n be sent and exit without calling the provider\n --llm-provider <name> anthropic | openai-compatible (default: anthropic)\n --llm-model <id> Model id for the BYOK provider\n --baseline <file> Suppress findings listed in a baseline file\n --write-baseline <file> Write current findings as a baseline, then exit 0\n --quiet Suppress the human summary banner\n --no-snippets Omit code snippets from the json/sarif report\n --color Force ANSI color in the human report\n --no-color Disable ANSI color (also: NO_COLOR env). Color is\n decoration only \u2014 every signal is printed as text\n -v, --version Print version and exit\n -h, --help Print this help and exit\n\nEXIT CODES\n 0 No findings at/above the threshold (or a baseline was written)\n 1 One or more findings at/above the severity threshold\n 2 Usage error or I/O failure\n\nEXAMPLES\n qscan . Scan the current directory\n qscan src --format sarif -o qscan.sarif\n qscan . --severity-threshold critical\n qscan . --write-baseline qscan-baseline.json\n qscan . --baseline qscan-baseline.json\n qscan . --include src --include lib\n qscan . --config ./ci/quantakrypto.config.json\n qscan . --changed --since origin/main\n qscan . --parallel --concurrency 4\n qscan . --cbom -o qscan-cbom.json\n";
8
+ export declare const HELP_TEXT = "qscan \u2014 find quantum-vulnerable cryptography in any codebase\n\nUSAGE\n qscan [path] [options]\n qscan hndl init [path] Scaffold an hndl.yml data map (see --hndl)\n qscan crypto-agility emit [path] Write a crypto-agility manifest (exits 0)\n qscan crypto-agility validate <file> Check a local manifest against the schema\n\nARGUMENTS\n path Directory or file to scan (default: \".\")\n\nOPTIONS\n --format <human|json|sarif|cbom|evidence|vex>\n Output format (default: human)\n --cbom Alias for --format cbom (CycloneDX CBOM)\n --format vex OpenVEX 0.2.0 document \u2014 one statement per rule,\n status \"affected\", with remediation + any\n --triage verdict for supply-chain VEX pipelines\n --merge <cbom.json> Merge an external CBOM (e.g. a qprobe endpoint\n CBOM) into the --cbom output via CycloneDX\n bom-link \u2014 one combined code + infra CBOM.\n Repeatable.\n --format evidence ISO 27001 A.8.24 readiness report \u2014 findings +\n inventory + CBOM + a deterministic content hash\n (sign/timestamp it with an external signer)\n --policy <file> Crypto-policy JSON; adds conformant/violation/\n transition verdicts to the evidence report\n --sign <command> Sign the evidence report: its contentHash is\n piped to <command> on stdin; stdout is recorded\n as the detached signature (needs --format evidence)\n --timestamp <command> Like --sign, but records an RFC-3161 timestamp\n token from <command> (needs --format evidence)\n -o, --output <file> Write the report to a file instead of stdout\n --severity-threshold <level> Fail (exit 1) on findings at/above this level;\n one of critical|high|medium|low|info\n (default: high)\n --no-source Skip scanning source files for inline crypto\n --no-deps Skip scanning dependency manifests\n --no-config Skip scanning config files (TLS/certificates)\n --config <path> Use this quantakrypto.config.json instead of\n auto-discovering one at the scan root\n --no-config-file Disable quantakrypto.config.json auto-discovery\n --ignore <pattern> Exclude paths matching <pattern> (repeatable)\n --include <pattern> Restrict the scan to matching paths (repeatable)\n --max-file-size <bytes> Skip files larger than <bytes> (default: 2 MiB)\n --no-default-ignores Don't skip node_modules/.git/dist by default\n --scan-minified Scan minified/generated/bundled files too\n --changed Scan only files changed in the git work tree\n --since <git-ref> With --changed, diff against <git-ref>\n --parallel Scan using a worker-thread pool when worthwhile\n --concurrency <n> Worker count for --parallel (implies --parallel);\n 0 forces the in-process serial path\n --top <n> List <n> findings in the human report (default: 5)\n --tier <category-3|category-5> Add CNSA migration targets to the report footer\n (category-5 = CNSA 2.0: ML-KEM-1024 / ML-DSA-87;\n an alias for --profile cnsa-2.0)\n --profile <id> Tailor migration guidance to a standards regime:\n nist (default) | cnsa-2.0 | bsi-tr-02102 | anssi |\n uk-ncsc. Sets the parameter sets, deadlines, and\n whether hybridization is required/recommended/optional\n --mandate <id> Gate findings against a compliance mandate's dated\n clauses (repeatable): cnsa-2.0 | nist-ir-8547. Reports\n each prohibited finding with its clause + deadline;\n fails the build only once a deadline has passed\n --lead-months <n> Fail early when a --mandate deadline is within n months\n --fail-now Fail on any --mandate-prohibited finding, ignoring the date\n --cache [file] Reuse findings for unchanged files across runs\n (default file: .quantakrypto-cache.json)\n --triage BYOK LLM pass that re-ranks findings by real\n exposure and explains them (never suppresses,\n never changes the exit code). Needs an API key in\n QK_LLM_API_KEY / ANTHROPIC_API_KEY / OPENAI_API_KEY\n --triage-floor <level> Only triage findings at/above this level (default: medium)\n --max-findings <n> Cap findings sent to the LLM during triage (default: 100; spend guard)\n --context <level> Source shared with the LLM: metadata|snippet|\n function|file (default: snippet; secrets always redacted)\n --dry-run With --triage, print the exact payload that would\n be sent and exit without calling the provider\n --llm-provider <name> anthropic | openai-compatible (default: anthropic)\n --llm-model <id> Model id for the BYOK provider\n --hndl Score harvest-now-decrypt-later exposure per\n finding + a repo summary from hndl.yml (data\n assets, classification, retention + secrecy\n lifetime vs the quantum-threat horizon; Mosca's\n inequality). Adds exposure fields to json/sarif;\n additive, never changes the exit code. Scaffold\n the map with \"qscan hndl init\". See docs/HNDL.md\n --crypto-agility Emit a crypto-agility posture manifest instead of a\n scan report (equivalent to \"crypto-agility emit\";\n always exits 0). A well-known-URL JSON document any\n agent/CI bot can read like security.txt: readiness\n score, quantum-vulnerable findings by severity, CBOM\n algorithm families, policy deadlines. Combine with\n --attestation / --hybrid-kex / --policy / -o.\n See docs/CRYPTO-AGILITY-MANIFEST.md\n --attestation <url> Record a posture-credential URL in the manifest\n (recorded verbatim, never fetched; offline boundary)\n --hybrid-kex / --no-hybrid-kex\n Assert hybrid post-quantum key exchange is / is not\n in use in the manifest (default: null / undetermined,\n since a static scan can't observe a negotiated group)\n --baseline <file> Suppress findings listed in a baseline file\n --write-baseline <file> Write current findings as a baseline, then exit 0\n --quiet Suppress the human summary banner\n --no-snippets Omit code snippets from the json/sarif report\n --color Force ANSI color in the human report\n --no-color Disable ANSI color (also: NO_COLOR env). Color is\n decoration only \u2014 every signal is printed as text\n -v, --version Print version and exit\n -h, --help Print this help and exit\n\nEXIT CODES\n 0 No findings at/above the threshold (or a baseline was written)\n 1 One or more findings at/above the severity threshold\n 2 Usage error or I/O failure\n\nEXAMPLES\n qscan . Scan the current directory\n qscan src --format sarif -o qscan.sarif\n qscan . --severity-threshold critical\n qscan . --write-baseline qscan-baseline.json\n qscan . --baseline qscan-baseline.json\n qscan . --include src --include lib\n qscan . --config ./ci/quantakrypto.config.json\n qscan . --changed --since origin/main\n qscan . --parallel --concurrency 4\n qscan . --cbom -o qscan-cbom.json\n qscan hndl init Scaffold hndl.yml seeded with detected assets\n qscan . --hndl --format json Emit per-finding HNDL exposure + a repo summary\n qscan . --crypto-agility -o .well-known/crypto-agility.json\n qscan crypto-agility validate .well-known/crypto-agility.json\n";
9
9
  /** The `--version` line. */
10
10
  export declare function versionLine(): string;
11
11
  //# sourceMappingURL=help.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../src/help.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,gCAAgC;AAChC,eAAO,MAAM,SAAS,yrMAiGrB,CAAC;AAEF,4BAA4B;AAC5B,wBAAgB,WAAW,IAAI,MAAM,CAEpC"}
1
+ {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../src/help.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,gCAAgC;AAChC,eAAO,MAAM,SAAS,+1RAmIrB,CAAC;AAEF,4BAA4B;AAC5B,wBAAgB,WAAW,IAAI,MAAM,CAEpC"}