@quantakrypto/qscan 0.4.4 → 0.5.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/README.md +37 -9
- package/dist/args.d.ts +28 -8
- package/dist/args.d.ts.map +1 -1
- package/dist/args.js +35 -5
- package/dist/args.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +18 -9
- package/dist/cli.js.map +1 -1
- package/dist/color.d.ts +38 -0
- package/dist/color.d.ts.map +1 -0
- package/dist/color.js +44 -0
- package/dist/color.js.map +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +41 -1
- package/dist/config.js.map +1 -1
- package/dist/help.d.ts +1 -1
- package/dist/help.d.ts.map +1 -1
- package/dist/help.js +22 -2
- package/dist/help.js.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +80 -22
- package/dist/index.js.map +1 -1
- package/dist/remediate-cli.d.ts +3 -4
- package/dist/remediate-cli.d.ts.map +1 -1
- package/dist/remediate-cli.js +1 -1
- package/dist/remediate-cli.js.map +1 -1
- package/dist/report.d.ts +13 -2
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +35 -6
- package/dist/report.js.map +1 -1
- package/dist/sign.d.ts +4 -0
- package/dist/sign.d.ts.map +1 -0
- package/dist/sign.js +54 -0
- package/dist/sign.js.map +1 -0
- package/dist/triage-run.d.ts +3 -4
- package/dist/triage-run.d.ts.map +1 -1
- package/dist/triage-run.js +13 -6
- package/dist/triage-run.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -14,7 +14,8 @@ step.
|
|
|
14
14
|
- **CI-friendly.** Severity thresholds drive the exit code; baselines suppress
|
|
15
15
|
known findings so the build only fails on *new* problems.
|
|
16
16
|
- **Multiple formats.** `human` (default), `json`, SARIF 2.1.0 for code-scanning
|
|
17
|
-
dashboards,
|
|
17
|
+
dashboards, a CycloneDX 1.6 **CBOM** for compliance tooling, an ISO 27001
|
|
18
|
+
A.8.24 **evidence** report, and an **OpenVEX** 0.2.0 document for VEX pipelines.
|
|
18
19
|
- **Fast on big repos.** Optional worker-thread parallelism (`--parallel`) and
|
|
19
20
|
git-aware incremental scanning (`--changed`).
|
|
20
21
|
|
|
@@ -23,10 +24,15 @@ step.
|
|
|
23
24
|
Inline crypto detection currently covers **JavaScript/TypeScript**, **Python**,
|
|
24
25
|
**Go**, **Java/Kotlin/Scala** (JCA + BouncyCastle), **C#/.NET**, **Rust**, **Ruby**,
|
|
25
26
|
**PHP** (openssl / phpseclib3 / libsodium), **Elixir** (`:crypto` / X509 / JOSE),
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
`
|
|
29
|
-
|
|
27
|
+
**C/C++** (OpenSSL, Mbed TLS, wolfSSL), **Swift** (CryptoKit / Security),
|
|
28
|
+
**Objective-C** (Security `SecKey*`), and **Dart/Flutter** (pointycastle /
|
|
29
|
+
`cryptography`), and **smart contracts** (Solidity/Move/Cairo on-chain
|
|
30
|
+
signature verification) source — **14 languages**. PEM key material, SSH keys (including
|
|
31
|
+
SSH-CA certificates), TLS/certificate config, reverse-proxy/gRPC TLS, WebAuthn/FIDO2,
|
|
32
|
+
code-signing, weak signature hashes (SHA-1/MD5), DKIM, SPIFFE/SPIRE, and dependency
|
|
33
|
+
manifests for **seven ecosystems** — npm (plus `yarn.lock` / `pnpm-lock.yaml`),
|
|
34
|
+
PyPI, Cargo, Go modules, Maven, RubyGems, and NuGet — are detected in **any**
|
|
35
|
+
file regardless of language.
|
|
30
36
|
|
|
31
37
|
qScan is **honest about coverage**: if a scan walks files but finds none in a
|
|
32
38
|
supported source language, it says so and will **not** present a bare `100/100`
|
|
@@ -56,7 +62,7 @@ qscan [path] [options]
|
|
|
56
62
|
|
|
57
63
|
| Flag | Description | Default |
|
|
58
64
|
| --- | --- | --- |
|
|
59
|
-
| `--format <human\|json\|sarif\|cbom>` | Output format. | `human` |
|
|
65
|
+
| `--format <human\|json\|sarif\|cbom\|evidence\|vex>` | Output format. | `human` |
|
|
60
66
|
| `--cbom` | Alias for `--format cbom` (CycloneDX 1.6 CBOM). | — |
|
|
61
67
|
| `-o, --output <file>` | Write the report to a file instead of stdout. | stdout |
|
|
62
68
|
| `--severity-threshold <level>` | Exit 1 if any finding is at/above this level. One of `critical`, `high`, `medium`, `low`, `info`. | `high` |
|
|
@@ -108,7 +114,7 @@ with `--config <path>`, or disable discovery with `--no-config-file`. See
|
|
|
108
114
|
|
|
109
115
|
```
|
|
110
116
|
qScan — quantum-vulnerable cryptography report
|
|
111
|
-
root: ./examples/vulnerable-app • files scanned: 2 • qscan v0.
|
|
117
|
+
root: ./examples/vulnerable-app • files scanned: 2 • qscan v0.5.0
|
|
112
118
|
|
|
113
119
|
3 findings (2 high, 1 medium)
|
|
114
120
|
2 exposed to harvest-now-decrypt-later (HNDL).
|
|
@@ -194,8 +200,11 @@ qscan . --concurrency 4 # pin the worker count (implies --parallel)
|
|
|
194
200
|
## CBOM (CycloneDX)
|
|
195
201
|
|
|
196
202
|
Emit a CycloneDX 1.6 **cryptographic bill of materials** — one
|
|
197
|
-
`cryptographic-asset` component per distinct (algorithm,
|
|
198
|
-
file:line occurrence evidence — for compliance and supply-chain tooling
|
|
203
|
+
`cryptographic-asset` component per distinct (assetType, algorithm, discriminator),
|
|
204
|
+
with file:line occurrence evidence — for compliance and supply-chain tooling.
|
|
205
|
+
Findings are classified into their proper CycloneDX `assetType`: `algorithm`
|
|
206
|
+
(crypto usage), `certificate` (X.509), `related-crypto-material` (private/public
|
|
207
|
+
key material), and `protocol` (TLS):
|
|
199
208
|
|
|
200
209
|
```bash
|
|
201
210
|
qscan . --cbom -o qscan-cbom.json
|
|
@@ -206,6 +215,25 @@ qscan . --format cbom -o qscan-cbom.json
|
|
|
206
215
|
The output is deterministic (sorted components and occurrences, stable serial
|
|
207
216
|
number), so re-running on an unchanged tree produces byte-identical CBOMs.
|
|
208
217
|
|
|
218
|
+
## VEX (OpenVEX)
|
|
219
|
+
|
|
220
|
+
Emit an **OpenVEX 0.2.0** document so the quantum-readiness posture flows into the
|
|
221
|
+
same supply-chain pipeline that already ingests CVE-based VEX:
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
qscan . --format vex -o qscan.openvex.json
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
One statement per rule (a synthetic `QK-<ruleId>` vulnerability), listing every
|
|
228
|
+
affected `file:line` product with `status: "affected"` and the rule's remediation
|
|
229
|
+
as the `action_statement`. PQC findings have no CVE, so qScan mints a stable
|
|
230
|
+
per-rule identifier rather than claiming one. qScan never reports `not_affected`
|
|
231
|
+
— only an operator can attest a mitigation — so downgrading a statement is left to
|
|
232
|
+
you to post-process. When `--triage` is also set, each verdict (exposure score /
|
|
233
|
+
priority / rationale) is carried in the statement's `status_notes`. Output is
|
|
234
|
+
deterministic (statements sorted by vulnerability, products deduped and sorted;
|
|
235
|
+
the `@id` derives from the finding set).
|
|
236
|
+
|
|
209
237
|
## Triage (opt-in, BYOK)
|
|
210
238
|
|
|
211
239
|
`--triage` adds an optional LLM pass that **re-ranks and explains** findings by
|
package/dist/args.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { meetsThreshold, SEVERITY_ORDER, severityRank } from "@quantakrypto/core";
|
|
11
11
|
import type { ContextLevel, ReportFormat, SecurityTier, Severity } from "@quantakrypto/core";
|
|
12
|
+
import type { ColorChoice } from "./color.js";
|
|
12
13
|
/** Valid `--llm-provider` values. */
|
|
13
14
|
declare const LLM_PROVIDERS: readonly ["anthropic", "openai-compatible"];
|
|
14
15
|
export type LlmProvider = (typeof LLM_PROVIDERS)[number];
|
|
@@ -16,9 +17,11 @@ export { meetsThreshold, SEVERITY_ORDER, severityRank };
|
|
|
16
17
|
/**
|
|
17
18
|
* Output formats qScan accepts on the command line. Extends core's
|
|
18
19
|
* {@link ReportFormat} with `"cbom"` (a CycloneDX cryptographic bill of
|
|
19
|
-
* materials),
|
|
20
|
+
* materials), `"evidence"` (ISO A.8.24 readiness report), and `"vex"` (an
|
|
21
|
+
* OpenVEX 0.2.0 document) — all rendered locally via core (`toCbom`,
|
|
22
|
+
* `buildReadinessReport`, `toOpenVex`).
|
|
20
23
|
*/
|
|
21
|
-
export type QscanFormat = ReportFormat | "cbom" | "evidence";
|
|
24
|
+
export type QscanFormat = ReportFormat | "cbom" | "evidence" | "vex";
|
|
22
25
|
/** Fully-resolved options the CLI/programmatic runner operates on. */
|
|
23
26
|
export interface QscanOptions {
|
|
24
27
|
/** Directory or file to scan. */
|
|
@@ -27,6 +30,8 @@ export interface QscanOptions {
|
|
|
27
30
|
format: QscanFormat;
|
|
28
31
|
/** Write the report to this file instead of stdout, when set. */
|
|
29
32
|
output?: string;
|
|
33
|
+
/** External CBOM files to merge into the `--cbom` output (CycloneDX bom-link). */
|
|
34
|
+
mergeCboms?: string[];
|
|
30
35
|
/** Findings at or above this severity cause a non-zero exit. */
|
|
31
36
|
severityThreshold: Severity;
|
|
32
37
|
/** Scan source files for inline crypto usage. */
|
|
@@ -68,6 +73,12 @@ export interface QscanOptions {
|
|
|
68
73
|
writeBaseline?: string;
|
|
69
74
|
/** Suppress the human summary banner (still writes reports/output files). */
|
|
70
75
|
quiet: boolean;
|
|
76
|
+
/**
|
|
77
|
+
* ANSI color policy for the human report (`--color` / `--no-color`). `"auto"`
|
|
78
|
+
* (default) colors only a live terminal and honors `NO_COLOR` / `FORCE_COLOR`;
|
|
79
|
+
* see {@link resolveColor}. Color is decoration only — never the sole signal.
|
|
80
|
+
*/
|
|
81
|
+
colorChoice: ColorChoice;
|
|
71
82
|
/** How many findings the human report lists (`--top N`). Default: 5. */
|
|
72
83
|
topN?: number;
|
|
73
84
|
/** Rule ids to suppress (from `quantakrypto.config.json` `disabledRules`). */
|
|
@@ -82,8 +93,23 @@ export interface QscanOptions {
|
|
|
82
93
|
maxFindings?: number;
|
|
83
94
|
/** CNSA security tier for the migration-targets footer (`--tier`). Default: none. */
|
|
84
95
|
tier?: SecurityTier;
|
|
96
|
+
/**
|
|
97
|
+
* Standards regime the migration guidance is tailored to (`--profile`): one of the
|
|
98
|
+
* built-in ids (nist / cnsa-2.0 / bsi-tr-02102 / anssi / uk-ncsc). Default: none
|
|
99
|
+
* (`--tier` maps to a profile for back-compat). Governs parameter sets, deadlines,
|
|
100
|
+
* and the hybrid stance surfaced in remediation.
|
|
101
|
+
*/
|
|
102
|
+
profile?: string;
|
|
85
103
|
/** Org cryptography policy file (`--policy`) for the evidence report's §4 verdicts. */
|
|
86
104
|
policy?: string;
|
|
105
|
+
/**
|
|
106
|
+
* External signer command for the evidence attestation (`--sign`). The report's
|
|
107
|
+
* contentHash is piped to it on stdin; its stdout is recorded as the detached
|
|
108
|
+
* signature. Only valid with `--format evidence`.
|
|
109
|
+
*/
|
|
110
|
+
sign?: string;
|
|
111
|
+
/** External RFC-3161 timestamper command for the evidence attestation (`--timestamp`). */
|
|
112
|
+
timestamp?: string;
|
|
87
113
|
/** How much source context leaves the machine (`--context`). Default: snippet. */
|
|
88
114
|
contextLevel?: ContextLevel;
|
|
89
115
|
/** Print the exact triage payload and exit without calling the provider (`--dry-run`). */
|
|
@@ -146,12 +172,6 @@ export declare const DEFAULT_CACHE_FILE = ".quantakrypto-cache.json";
|
|
|
146
172
|
export declare function parseArgs(argv: readonly string[]): ParsedArgs;
|
|
147
173
|
/** Validate/normalize a `--format` value. */
|
|
148
174
|
export declare function asFormat(value: string): QscanFormat;
|
|
149
|
-
/** Validate/normalize a `--context` level. */
|
|
150
|
-
export declare function asContextLevel(value: string): ContextLevel;
|
|
151
|
-
/** Validate/normalize a `--tier` value. */
|
|
152
|
-
export declare function asTier(value: string): SecurityTier;
|
|
153
|
-
/** Validate/normalize a `--llm-provider` value. */
|
|
154
|
-
export declare function asProvider(value: string): LlmProvider;
|
|
155
175
|
/** Validate/normalize a non-negative integer flag value. */
|
|
156
176
|
export declare function asInt(value: string, flag: string): number;
|
|
157
177
|
/** Validate/normalize a severity value. */
|
package/dist/args.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
|
package/dist/args.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* (`-o`, `-v`, `-h`), repeatable `--ignore`, and a single optional positional
|
|
8
8
|
* path. Unknown flags are a usage error.
|
|
9
9
|
*/
|
|
10
|
-
import { meetsThreshold, SEVERITY_ORDER, severityRank } from "@quantakrypto/core";
|
|
10
|
+
import { meetsThreshold, SEVERITY_ORDER, severityRank, standardsProfileIds, } from "@quantakrypto/core";
|
|
11
11
|
/** Valid context levels for `--context` (how much source triage/remediate sends). */
|
|
12
12
|
const CONTEXT_LEVELS = ["metadata", "snippet", "function", "file"];
|
|
13
13
|
/** Valid CNSA security tiers for `--tier` (report migration-target guidance). */
|
|
@@ -19,7 +19,7 @@ const LLM_PROVIDERS = ["anthropic", "openai-compatible"];
|
|
|
19
19
|
// `@quantakrypto/qscan` callers (and tests) keep importing them from `./args.js`
|
|
20
20
|
// without qScan maintaining a second, drift-prone copy.
|
|
21
21
|
export { meetsThreshold, SEVERITY_ORDER, severityRank };
|
|
22
|
-
const FORMATS = ["human", "json", "sarif", "cbom", "evidence"];
|
|
22
|
+
const FORMATS = ["human", "json", "sarif", "cbom", "evidence", "vex"];
|
|
23
23
|
/** Thrown on malformed input; the CLI maps this to exit code 2. */
|
|
24
24
|
export class ArgError extends Error {
|
|
25
25
|
name = "ArgError";
|
|
@@ -40,6 +40,7 @@ export function defaultOptions() {
|
|
|
40
40
|
changed: false,
|
|
41
41
|
parallel: false,
|
|
42
42
|
quiet: false,
|
|
43
|
+
colorChoice: "auto",
|
|
43
44
|
noSnippets: false,
|
|
44
45
|
noConfigFile: false,
|
|
45
46
|
triage: false,
|
|
@@ -103,6 +104,11 @@ export function parseArgs(argv) {
|
|
|
103
104
|
rejectInlineValue();
|
|
104
105
|
options.format = "cbom";
|
|
105
106
|
break;
|
|
107
|
+
case "--merge":
|
|
108
|
+
// Merge an external CBOM (e.g. a qprobe endpoint CBOM) into the --cbom
|
|
109
|
+
// output via CycloneDX bom-link. Repeatable.
|
|
110
|
+
(options.mergeCboms ??= []).push(takeValue());
|
|
111
|
+
break;
|
|
106
112
|
case "-o":
|
|
107
113
|
case "--output":
|
|
108
114
|
options.output = takeValue();
|
|
@@ -154,6 +160,9 @@ export function parseArgs(argv) {
|
|
|
154
160
|
case "--tier":
|
|
155
161
|
options.tier = asTier(takeValue());
|
|
156
162
|
break;
|
|
163
|
+
case "--profile":
|
|
164
|
+
options.profile = asProfile(takeValue());
|
|
165
|
+
break;
|
|
157
166
|
case "--context":
|
|
158
167
|
options.contextLevel = asContextLevel(takeValue());
|
|
159
168
|
break;
|
|
@@ -232,6 +241,12 @@ export function parseArgs(argv) {
|
|
|
232
241
|
case "--policy":
|
|
233
242
|
options.policy = takeValue();
|
|
234
243
|
break;
|
|
244
|
+
case "--sign":
|
|
245
|
+
options.sign = takeValue();
|
|
246
|
+
break;
|
|
247
|
+
case "--timestamp":
|
|
248
|
+
options.timestamp = takeValue();
|
|
249
|
+
break;
|
|
235
250
|
case "--write-baseline":
|
|
236
251
|
options.writeBaseline = takeValue();
|
|
237
252
|
break;
|
|
@@ -239,6 +254,14 @@ export function parseArgs(argv) {
|
|
|
239
254
|
rejectInlineValue();
|
|
240
255
|
options.quiet = true;
|
|
241
256
|
break;
|
|
257
|
+
case "--color":
|
|
258
|
+
rejectInlineValue();
|
|
259
|
+
options.colorChoice = "always";
|
|
260
|
+
break;
|
|
261
|
+
case "--no-color":
|
|
262
|
+
rejectInlineValue();
|
|
263
|
+
options.colorChoice = "never";
|
|
264
|
+
break;
|
|
242
265
|
case "--no-snippets":
|
|
243
266
|
rejectInlineValue();
|
|
244
267
|
options.noSnippets = true;
|
|
@@ -265,19 +288,26 @@ export function asFormat(value) {
|
|
|
265
288
|
throw new ArgError(`invalid --format "${value}" (expected one of: ${FORMATS.join(", ")})`);
|
|
266
289
|
}
|
|
267
290
|
/** Validate/normalize a `--context` level. */
|
|
268
|
-
|
|
291
|
+
function asContextLevel(value) {
|
|
269
292
|
if (CONTEXT_LEVELS.includes(value))
|
|
270
293
|
return value;
|
|
271
294
|
throw new ArgError(`invalid --context "${value}" (expected one of: ${CONTEXT_LEVELS.join(", ")})`);
|
|
272
295
|
}
|
|
273
296
|
/** Validate/normalize a `--tier` value. */
|
|
274
|
-
|
|
297
|
+
function asTier(value) {
|
|
275
298
|
if (SECURITY_TIERS.includes(value))
|
|
276
299
|
return value;
|
|
277
300
|
throw new ArgError(`invalid --tier "${value}" (expected one of: ${SECURITY_TIERS.join(", ")})`);
|
|
278
301
|
}
|
|
302
|
+
/** Validate a `--profile` value against the built-in standards regimes. */
|
|
303
|
+
function asProfile(value) {
|
|
304
|
+
const ids = standardsProfileIds();
|
|
305
|
+
if (ids.includes(value))
|
|
306
|
+
return value;
|
|
307
|
+
throw new ArgError(`invalid --profile "${value}" (expected one of: ${ids.join(", ")})`);
|
|
308
|
+
}
|
|
279
309
|
/** Validate/normalize a `--llm-provider` value. */
|
|
280
|
-
|
|
310
|
+
function asProvider(value) {
|
|
281
311
|
if (LLM_PROVIDERS.includes(value))
|
|
282
312
|
return value;
|
|
283
313
|
throw new ArgError(`invalid --llm-provider "${value}" (expected one of: ${LLM_PROVIDERS.join(", ")})`);
|
package/dist/args.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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 { meetsThreshold, SEVERITY_ORDER, severityRank } from \"@quantakrypto/core\";\nimport type { ContextLevel, ReportFormat, SecurityTier, Severity } from \"@quantakrypto/core\";\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), which qScan renders locally via core's `toCbom`.\n */\nexport type QscanFormat = ReportFormat | \"cbom\" | \"evidence\";\n\nconst FORMATS: readonly QscanFormat[] = [\"human\", \"json\", \"sarif\", \"cbom\", \"evidence\"];\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 /** 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 /** 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 /** Org cryptography policy file (`--policy`) for the evidence report's §4 verdicts. */\n policy?: 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 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 \"-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 \"--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 \"--write-baseline\":\n options.writeBaseline = takeValue();\n break;\n case \"--quiet\":\n rejectInlineValue();\n options.quiet = true;\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. */\nexport function 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. */\nexport function 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/normalize a `--llm-provider` value. */\nexport function 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 /** 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"]}
|
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;
|
|
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"}
|
package/dist/cli.js
CHANGED
|
@@ -14,6 +14,7 @@ import process from "node:process";
|
|
|
14
14
|
import { fileURLToPath } from "node:url";
|
|
15
15
|
import { ConfigError } from "@quantakrypto/core";
|
|
16
16
|
import { ArgError, parseArgs } from "./args.js";
|
|
17
|
+
import { resolveColor } from "./color.js";
|
|
17
18
|
import { resolveConfig } from "./config.js";
|
|
18
19
|
import { HELP_TEXT, versionLine } from "./help.js";
|
|
19
20
|
import { EXIT, runQscan } from "./index.js";
|
|
@@ -62,21 +63,29 @@ export async function main(argv) {
|
|
|
62
63
|
process.stderr.write(`qscan: ${message}\n`);
|
|
63
64
|
return EXIT.ERROR;
|
|
64
65
|
}
|
|
65
|
-
// Color
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
// Color policy: --color/--no-color > NO_COLOR/FORCE_COLOR > interactive stdout.
|
|
67
|
+
// Color is decoration only (every signal is also text), so this is purely an
|
|
68
|
+
// accessibility / pipe-safety control. See resolveColor for the precedence.
|
|
69
|
+
const color = resolveColor({
|
|
70
|
+
choice: options.colorChoice,
|
|
71
|
+
format: options.format,
|
|
72
|
+
toFile: Boolean(options.output),
|
|
73
|
+
isTTY: Boolean(process.stdout.isTTY),
|
|
74
|
+
env: { NO_COLOR: process.env.NO_COLOR, FORCE_COLOR: process.env.FORCE_COLOR },
|
|
75
|
+
});
|
|
70
76
|
let run;
|
|
71
77
|
try {
|
|
72
78
|
run = await runQscan(options, { color });
|
|
73
79
|
}
|
|
74
80
|
catch (err) {
|
|
75
|
-
// A missing
|
|
76
|
-
//
|
|
77
|
-
//
|
|
81
|
+
// A missing file is the most common failure; a raw "ENOENT: no such file or
|
|
82
|
+
// directory, stat '…'" reads like a tool bug. Turn it into a plain, actionable
|
|
83
|
+
// line (still exit 2). Report the ACTUAL missing path from `err.path` when present
|
|
84
|
+
// — an ENOENT can come from `--policy`, `--baseline`, or `--write-baseline`, not
|
|
85
|
+
// just the scan path, and blaming the (existing) scan path misdirects the user.
|
|
78
86
|
if (isErrno(err) && err.code === "ENOENT") {
|
|
79
|
-
|
|
87
|
+
const missing = typeof err.path === "string" && err.path ? err.path : options.path;
|
|
88
|
+
process.stderr.write(`qscan: path not found: ${missing}\n`);
|
|
80
89
|
return EXIT.ERROR;
|
|
81
90
|
}
|
|
82
91
|
const message = err instanceof Error ? err.message : String(err);
|
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 { 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 only when writing the human report to an interactive stdout.\n const color =\n options.format === \"human\" &&\n !options.output &&\n Boolean(process.stdout.isTTY) &&\n process.env.NO_COLOR === undefined;\n\n let run: QscanRun;\n try {\n run = await runQscan(options, { color });\n } catch (err) {\n // A missing scan path is the most common failure; a raw\n // \"ENOENT: no such file or directory, stat '…'\" reads like a tool bug.\n // Turn it into a plain, actionable line (still exit 2).\n if (isErrno(err) && err.code === \"ENOENT\") {\n process.stderr.write(`qscan: path not found: ${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 // --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 { 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"]}
|
package/dist/color.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Color-output policy for the human report.
|
|
3
|
+
*
|
|
4
|
+
* ANSI color is decoration, never the sole carrier of meaning (every severity,
|
|
5
|
+
* count, and score is also printed as text), so turning it off never loses
|
|
6
|
+
* information — it's an accessibility and pipe-safety control. This module is the
|
|
7
|
+
* single place that decides whether to emit it, with an explicit precedence so
|
|
8
|
+
* the behaviour is predictable and testable:
|
|
9
|
+
*
|
|
10
|
+
* 1. Non-`human` formats are NEVER colored — ANSI would corrupt JSON/SARIF/CBOM.
|
|
11
|
+
* 2. An explicit `--color` / `--no-color` flag wins over everything else.
|
|
12
|
+
* 3. `NO_COLOR` (present, non-empty) disables — https://no-color.org.
|
|
13
|
+
* 4. `FORCE_COLOR` enables (Node/supports-color convention: `0`/`false` disable,
|
|
14
|
+
* any other value — including empty — enables).
|
|
15
|
+
* 5. Otherwise: color only a live terminal (`stdout.isTTY`), never a file or pipe.
|
|
16
|
+
*/
|
|
17
|
+
/** What the user asked for on the command line. `"auto"` = decide from context. */
|
|
18
|
+
export type ColorChoice = "always" | "never" | "auto";
|
|
19
|
+
/** Just the environment variables the decision reads. */
|
|
20
|
+
interface ColorEnv {
|
|
21
|
+
NO_COLOR?: string | undefined;
|
|
22
|
+
FORCE_COLOR?: string | undefined;
|
|
23
|
+
}
|
|
24
|
+
export interface ColorContext {
|
|
25
|
+
/** From `--color` / `--no-color`; defaults to `"auto"`. */
|
|
26
|
+
choice: ColorChoice;
|
|
27
|
+
/** Report format; only `"human"` is ever colored. */
|
|
28
|
+
format: string;
|
|
29
|
+
/** True when the report goes to an output file rather than stdout. */
|
|
30
|
+
toFile: boolean;
|
|
31
|
+
/** `process.stdout.isTTY`. */
|
|
32
|
+
isTTY: boolean;
|
|
33
|
+
env: ColorEnv;
|
|
34
|
+
}
|
|
35
|
+
/** Resolve whether the human report should emit ANSI color. */
|
|
36
|
+
export declare function resolveColor(ctx: ColorContext): boolean;
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=color.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color.d.ts","sourceRoot":"","sources":["../src/color.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,mFAAmF;AACnF,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAEtD,yDAAyD;AACzD,UAAU,QAAQ;IAChB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAED,MAAM,WAAW,YAAY;IAC3B,2DAA2D;IAC3D,MAAM,EAAE,WAAW,CAAC;IACpB,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,MAAM,EAAE,OAAO,CAAC;IAChB,8BAA8B;IAC9B,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,QAAQ,CAAC;CACf;AAYD,+DAA+D;AAC/D,wBAAgB,YAAY,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAgBvD"}
|