@quantakrypto/qscan 0.1.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 +251 -0
- package/dist/args.d.ts +128 -0
- package/dist/args.d.ts.map +1 -0
- package/dist/args.js +200 -0
- package/dist/args.js.map +1 -0
- package/dist/baseline.d.ts +60 -0
- package/dist/baseline.d.ts.map +1 -0
- package/dist/baseline.js +105 -0
- package/dist/baseline.js.map +1 -0
- package/dist/cli.d.ts +13 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +121 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +42 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +78 -0
- package/dist/config.js.map +1 -0
- package/dist/help.d.ts +11 -0
- package/dist/help.d.ts.map +1 -0
- package/dist/help.js +67 -0
- package/dist/help.js.map +1 -0
- package/dist/index.d.ts +99 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +133 -0
- package/dist/index.js.map +1 -0
- package/dist/report.d.ts +46 -0
- package/dist/report.d.ts.map +1 -0
- package/dist/report.js +204 -0
- package/dist/report.js.map +1 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# @quantakrypto/qscan
|
|
2
|
+
|
|
3
|
+
**Find quantum-vulnerable cryptography in any codebase.**
|
|
4
|
+
|
|
5
|
+
`qscan` walks a project and reports where it relies on classical asymmetric
|
|
6
|
+
cryptography (RSA, (EC)DH, ECDSA, EdDSA, DSA, …) — the algorithms broken by a
|
|
7
|
+
sufficiently large quantum computer, and the ones exposed to *harvest-now,
|
|
8
|
+
decrypt-later* (HNDL) attacks today. It scans source files, dependency
|
|
9
|
+
manifests, and configuration, then prints a readiness score and a concrete next
|
|
10
|
+
step.
|
|
11
|
+
|
|
12
|
+
- **Zero runtime dependencies.** Node built-ins only; the engine is
|
|
13
|
+
[`@quantakrypto/core`](../core).
|
|
14
|
+
- **CI-friendly.** Severity thresholds drive the exit code; baselines suppress
|
|
15
|
+
known findings so the build only fails on *new* problems.
|
|
16
|
+
- **Multiple formats.** `human` (default), `json`, SARIF 2.1.0 for code-scanning
|
|
17
|
+
dashboards, and a CycloneDX 1.6 **CBOM** for compliance tooling.
|
|
18
|
+
- **Fast on big repos.** Optional worker-thread parallelism (`--parallel`) and
|
|
19
|
+
git-aware incremental scanning (`--changed`).
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install -g @quantakrypto/qscan
|
|
25
|
+
# or run without installing
|
|
26
|
+
npx @quantakrypto/qscan .
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Requires Node ≥ 20.
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
qscan [path] [options]
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`path` defaults to the current directory (`.`).
|
|
38
|
+
|
|
39
|
+
### Options
|
|
40
|
+
|
|
41
|
+
| Flag | Description | Default |
|
|
42
|
+
| --- | --- | --- |
|
|
43
|
+
| `--format <human\|json\|sarif\|cbom>` | Output format. | `human` |
|
|
44
|
+
| `--cbom` | Alias for `--format cbom` (CycloneDX 1.6 CBOM). | — |
|
|
45
|
+
| `-o, --output <file>` | Write the report to a file instead of stdout. | stdout |
|
|
46
|
+
| `--severity-threshold <level>` | Exit 1 if any finding is at/above this level. One of `critical`, `high`, `medium`, `low`, `info`. | `high` |
|
|
47
|
+
| `--no-source` | Skip scanning source files for inline crypto. | scan on |
|
|
48
|
+
| `--no-deps` | Skip scanning dependency manifests. | scan on |
|
|
49
|
+
| `--no-config` | Skip scanning config files (TLS/certificates). Toggles config *detectors* — not the config file below. | scan on |
|
|
50
|
+
| `--config <path>` | Use this `quantakrypto.config.json` instead of auto-discovering one at the scan root. | auto |
|
|
51
|
+
| `--no-config-file` | Disable `quantakrypto.config.json` auto-discovery. | discovery on |
|
|
52
|
+
| `--ignore <pattern>` | Exclude paths matching `<pattern>`. Repeatable. | — |
|
|
53
|
+
| `--include <pattern>` | Restrict the scan to paths matching `<pattern>`. Repeatable. | all files |
|
|
54
|
+
| `--max-file-size <bytes>` | Skip files larger than `<bytes>`. | 2 MiB |
|
|
55
|
+
| `--no-default-ignores` | Don't skip `node_modules`/`.git`/`dist` by default. | ignores on |
|
|
56
|
+
| `--scan-minified` | Scan minified/generated/bundled files too. | skipped |
|
|
57
|
+
| `--changed` | Incremental: scan only files git reports as changed. | off |
|
|
58
|
+
| `--since <git-ref>` | With `--changed`, diff against `<git-ref>` (implies `--changed`). | working tree |
|
|
59
|
+
| `--parallel` | Scan using a worker-thread pool when the workload is large enough. | off |
|
|
60
|
+
| `--concurrency <n>` | Worker count for `--parallel` (implies `--parallel`). `0`/`1` forces serial. | CPU count |
|
|
61
|
+
| `--baseline <file>` | Suppress findings whose fingerprint is in the baseline file. | — |
|
|
62
|
+
| `--write-baseline <file>` | Write current findings as a baseline, then exit 0. | — |
|
|
63
|
+
| `--quiet` | Suppress the human summary banner. | off |
|
|
64
|
+
| `-v, --version` | Print version and exit. | — |
|
|
65
|
+
| `-h, --help` | Print help and exit. | — |
|
|
66
|
+
|
|
67
|
+
### Exit codes
|
|
68
|
+
|
|
69
|
+
| Code | Meaning |
|
|
70
|
+
| --- | --- |
|
|
71
|
+
| `0` | No findings at/above the threshold — or a baseline was written. |
|
|
72
|
+
| `1` | One or more findings at/above the severity threshold. |
|
|
73
|
+
| `2` | Usage error or I/O failure. |
|
|
74
|
+
|
|
75
|
+
### Config file (`quantakrypto.config.json`)
|
|
76
|
+
|
|
77
|
+
qScan auto-discovers a `quantakrypto.config.json` at the scan root and applies it
|
|
78
|
+
under a strict precedence: **CLI flags > config file > built-in defaults**
|
|
79
|
+
(per-key). It encodes `include` / `exclude` / `maxFileSize` / `noDefaultIgnores`
|
|
80
|
+
/ `scanMinified`, the detector-family toggles, `severityThreshold`, and a
|
|
81
|
+
`baseline` path so CI and local runs share one policy. Point at a file elsewhere
|
|
82
|
+
with `--config <path>`, or disable discovery with `--no-config-file`. See
|
|
83
|
+
[docs/CONFIG.md](../../docs/CONFIG.md) for the full schema.
|
|
84
|
+
|
|
85
|
+
## Example output
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
qScan — quantum-vulnerable cryptography report
|
|
89
|
+
root: ./examples/vulnerable-app • files scanned: 2 • qscan v0.1.0
|
|
90
|
+
|
|
91
|
+
3 findings (2 high, 1 medium)
|
|
92
|
+
2 exposed to harvest-now-decrypt-later (HNDL).
|
|
93
|
+
Readiness score: 70/100
|
|
94
|
+
|
|
95
|
+
Top findings
|
|
96
|
+
high rsa-keygen src/crypto.js:5
|
|
97
|
+
RSA is not quantum-safe.
|
|
98
|
+
→ Use ML-KEM-768 (hybrid X25519MLKEM768).
|
|
99
|
+
high dep-vulnerable package.json:7
|
|
100
|
+
Dependency "node-forge" provides classical asymmetric crypto.
|
|
101
|
+
medium ecdh-usage src/crypto.js:13
|
|
102
|
+
ECDH is not quantum-safe.
|
|
103
|
+
→ Use a hybrid KEM (X25519MLKEM768).
|
|
104
|
+
|
|
105
|
+
Next step: migrate src/crypto.js — Use ML-KEM-768 (hybrid X25519MLKEM768).
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Color is emitted only when writing the human format to an interactive terminal
|
|
109
|
+
(and is suppressed by the `NO_COLOR` environment variable). Reports written to a
|
|
110
|
+
file or piped are always plain text.
|
|
111
|
+
|
|
112
|
+
## Baselines
|
|
113
|
+
|
|
114
|
+
A baseline records the **fingerprints** of findings you have already triaged.
|
|
115
|
+
qScan uses the **canonical baseline** shared across the whole monorepo
|
|
116
|
+
(`@quantakrypto/core`, the GitHub Action, and this CLI): a single on-disk format and a
|
|
117
|
+
single fingerprint algorithm, so a baseline written by one tool is understood by
|
|
118
|
+
the others.
|
|
119
|
+
|
|
120
|
+
A fingerprint is a full SHA-256 of `ruleId|file|normalizedSnippet`. It is
|
|
121
|
+
deliberately **line-insensitive** — unrelated edits that shift line numbers no
|
|
122
|
+
longer invalidate it — and the snippet's whitespace is normalized so
|
|
123
|
+
reformatting doesn't either. It ignores volatile fields like severity wording or
|
|
124
|
+
timestamps.
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
# 1. Accept the current state of the world.
|
|
128
|
+
qscan . --write-baseline qscan-baseline.json
|
|
129
|
+
|
|
130
|
+
# 2. From now on, fail only on findings that are NOT in the baseline.
|
|
131
|
+
qscan . --baseline qscan-baseline.json
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
The baseline file is plain JSON:
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{
|
|
138
|
+
"version": 1,
|
|
139
|
+
"fingerprints": [
|
|
140
|
+
"0f1e2d3c4b5a...full-sha256...",
|
|
141
|
+
"a1b2c3d4e5f6...full-sha256..."
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Incremental scans
|
|
147
|
+
|
|
148
|
+
In CI you usually only care about what a change introduced. `--changed` restricts
|
|
149
|
+
the scan to the files git reports as modified, which is a large win on big repos:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
# Files modified in the working tree (staged, unstaged, and untracked).
|
|
153
|
+
qscan . --changed
|
|
154
|
+
|
|
155
|
+
# Everything that changed since a base ref (e.g. a PR base).
|
|
156
|
+
qscan . --changed --since origin/main
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Outside a git work tree the changed-file list is empty (nothing is scanned),
|
|
160
|
+
rather than an error.
|
|
161
|
+
|
|
162
|
+
## Parallel scans
|
|
163
|
+
|
|
164
|
+
For large trees, route the scan through a worker-thread pool. qScan automatically
|
|
165
|
+
stays serial for small inputs, so `--parallel` is safe to leave on:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
qscan . --parallel # auto worker count (CPU cores)
|
|
169
|
+
qscan . --concurrency 4 # pin the worker count (implies --parallel)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## CBOM (CycloneDX)
|
|
173
|
+
|
|
174
|
+
Emit a CycloneDX 1.6 **cryptographic bill of materials** — one
|
|
175
|
+
`cryptographic-asset` component per distinct (algorithm, primitive) pair, with
|
|
176
|
+
file:line occurrence evidence — for compliance and supply-chain tooling:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
qscan . --cbom -o qscan-cbom.json
|
|
180
|
+
# equivalently:
|
|
181
|
+
qscan . --format cbom -o qscan-cbom.json
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
The output is deterministic (sorted components and occurrences, stable serial
|
|
185
|
+
number), so re-running on an unchanged tree produces byte-identical CBOMs.
|
|
186
|
+
|
|
187
|
+
## CI
|
|
188
|
+
|
|
189
|
+
Fail the build on any high-or-worse finding, while tolerating an accepted
|
|
190
|
+
baseline:
|
|
191
|
+
|
|
192
|
+
```yaml
|
|
193
|
+
# .github/workflows/qscan.yml
|
|
194
|
+
name: qscan
|
|
195
|
+
on: [push, pull_request]
|
|
196
|
+
|
|
197
|
+
jobs:
|
|
198
|
+
quantum-readiness:
|
|
199
|
+
runs-on: ubuntu-latest
|
|
200
|
+
steps:
|
|
201
|
+
- uses: actions/checkout@v4
|
|
202
|
+
- uses: actions/setup-node@v4
|
|
203
|
+
with:
|
|
204
|
+
node-version: 20
|
|
205
|
+
- name: Scan for quantum-vulnerable crypto
|
|
206
|
+
run: npx @quantakrypto/qscan . --severity-threshold high --baseline qscan-baseline.json
|
|
207
|
+
|
|
208
|
+
# Optional: upload SARIF to GitHub code scanning.
|
|
209
|
+
- name: Generate SARIF
|
|
210
|
+
if: always()
|
|
211
|
+
run: npx @quantakrypto/qscan . --format sarif -o qscan.sarif
|
|
212
|
+
- uses: github/codeql-action/upload-sarif@v3
|
|
213
|
+
if: always()
|
|
214
|
+
with:
|
|
215
|
+
sarif_file: qscan.sarif
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Programmatic API
|
|
219
|
+
|
|
220
|
+
The CLI is a thin shell over `runQscan`, which `@quantakrypto/action` reuses:
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
import { runQscan, EXIT } from "@quantakrypto/qscan";
|
|
224
|
+
|
|
225
|
+
const { result, exitCode, suppressed } = await runQscan({
|
|
226
|
+
path: "src",
|
|
227
|
+
format: "json",
|
|
228
|
+
severityThreshold: "high",
|
|
229
|
+
baseline: "qscan-baseline.json",
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
console.log(`${result.findings.length} findings, exit ${exitCode}`);
|
|
233
|
+
if (exitCode === EXIT.FINDINGS) process.exitCode = 1;
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
`runQscan` never touches `process` or stdout — it returns the rendered `report`
|
|
237
|
+
string and a suggested `exitCode`, leaving I/O to the caller. The package also
|
|
238
|
+
re-exports the argument parser (`parseArgs`, `defaultOptions`), severity helpers
|
|
239
|
+
(`severityRank`, `meetsThreshold`), and the **canonical** baseline utilities from
|
|
240
|
+
`@quantakrypto/core` (`fingerprintFinding`, `baselineFromFindings`, `applyBaseline`,
|
|
241
|
+
`loadBaseline`, `saveBaseline`) plus their legacy aliases (`fingerprint`,
|
|
242
|
+
`buildBaseline`, `readBaseline`, `writeBaseline`) for source compatibility.
|
|
243
|
+
|
|
244
|
+
## Examples
|
|
245
|
+
|
|
246
|
+
See [`examples/`](./examples) for a sample vulnerable project and the commands
|
|
247
|
+
to scan it.
|
|
248
|
+
|
|
249
|
+
## License
|
|
250
|
+
|
|
251
|
+
Apache-2.0
|
package/dist/args.d.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zero-dependency command-line argument parsing for qScan.
|
|
3
|
+
*
|
|
4
|
+
* Hand-rolled rather than pulled from a library to keep the package free of
|
|
5
|
+
* runtime dependencies. The grammar is deliberately small: long flags
|
|
6
|
+
* (`--flag`, `--flag value`, `--flag=value`), a couple of short aliases
|
|
7
|
+
* (`-o`, `-v`, `-h`), repeatable `--ignore`, and a single optional positional
|
|
8
|
+
* path. Unknown flags are a usage error.
|
|
9
|
+
*/
|
|
10
|
+
import type { ReportFormat, Severity } from "@quantakrypto/core";
|
|
11
|
+
/** Severities ordered most → least severe; index 0 is the most severe. */
|
|
12
|
+
export declare const SEVERITY_ORDER: readonly Severity[];
|
|
13
|
+
/**
|
|
14
|
+
* Output formats qScan accepts on the command line. Extends core's
|
|
15
|
+
* {@link ReportFormat} with `"cbom"` (a CycloneDX cryptographic bill of
|
|
16
|
+
* materials), which qScan renders locally via core's `toCbom`.
|
|
17
|
+
*/
|
|
18
|
+
export type QscanFormat = ReportFormat | "cbom";
|
|
19
|
+
/** Fully-resolved options the CLI/programmatic runner operates on. */
|
|
20
|
+
export interface QscanOptions {
|
|
21
|
+
/** Directory or file to scan. */
|
|
22
|
+
path: string;
|
|
23
|
+
/** Report format. */
|
|
24
|
+
format: QscanFormat;
|
|
25
|
+
/** Write the report to this file instead of stdout, when set. */
|
|
26
|
+
output?: string;
|
|
27
|
+
/** Findings at or above this severity cause a non-zero exit. */
|
|
28
|
+
severityThreshold: Severity;
|
|
29
|
+
/** Scan source files for inline crypto usage. */
|
|
30
|
+
source: boolean;
|
|
31
|
+
/** Scan dependency manifests for vulnerable libraries. */
|
|
32
|
+
dependencies: boolean;
|
|
33
|
+
/** Scan config files (TLS/certificates). */
|
|
34
|
+
config: boolean;
|
|
35
|
+
/** Extra exclude patterns (repeatable `--ignore`). */
|
|
36
|
+
ignore: string[];
|
|
37
|
+
/**
|
|
38
|
+
* Restrict the walk to paths matching one of these include patterns
|
|
39
|
+
* (repeatable `--include`). When empty, every non-excluded file is scanned.
|
|
40
|
+
*/
|
|
41
|
+
include: string[];
|
|
42
|
+
/** Max file size to read, in bytes (`--max-file-size`). */
|
|
43
|
+
maxFileSize?: number;
|
|
44
|
+
/** Disable the built-in ignore list (`--no-default-ignores`). */
|
|
45
|
+
noDefaultIgnores: boolean;
|
|
46
|
+
/** Scan minified/generated/bundled files instead of skipping them. */
|
|
47
|
+
scanMinified: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Incremental mode: scan only the files git reports as changed
|
|
50
|
+
* (`--changed`), optionally relative to {@link since}.
|
|
51
|
+
*/
|
|
52
|
+
changed: boolean;
|
|
53
|
+
/** Git ref/range the `--changed` diff is taken against (`--since`). */
|
|
54
|
+
since?: string;
|
|
55
|
+
/** Route the scan through core's worker-thread pool (`--parallel`). */
|
|
56
|
+
parallel: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Worker count for parallel scanning (`--concurrency`). Implies parallel.
|
|
59
|
+
* A value of 0 or 1 forces the in-process serial path.
|
|
60
|
+
*/
|
|
61
|
+
concurrency?: number;
|
|
62
|
+
/** Suppress findings whose fingerprint is in this baseline file. */
|
|
63
|
+
baseline?: string;
|
|
64
|
+
/** Write current findings as a baseline to this file, then exit 0. */
|
|
65
|
+
writeBaseline?: string;
|
|
66
|
+
/** Suppress the human summary banner (still writes reports/output files). */
|
|
67
|
+
quiet: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Explicit path to a `quantakrypto.config.json` (`--config <path>`). Overrides
|
|
70
|
+
* auto-discovery at the scan root. Distinct from `--no-config`, which toggles
|
|
71
|
+
* config/TLS *detector* scanning — this names the config FILE.
|
|
72
|
+
*/
|
|
73
|
+
configFile?: string;
|
|
74
|
+
/**
|
|
75
|
+
* Disable `quantakrypto.config.json` auto-discovery (`--no-config-file`). Distinct
|
|
76
|
+
* from `--no-config` (which skips config-file *detectors*).
|
|
77
|
+
*/
|
|
78
|
+
noConfigFile: boolean;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Option keys that a `quantakrypto.config.json` may also set. When such a key was set
|
|
82
|
+
* by a CLI flag, the flag wins (precedence: flags > config > defaults); when it
|
|
83
|
+
* was left at its default, config may fill it. {@link parseArgs} records which
|
|
84
|
+
* of these keys came from an explicit flag in {@link ParsedRun.explicit}.
|
|
85
|
+
*/
|
|
86
|
+
export type ConfigurableKey = "severityThreshold" | "source" | "dependencies" | "config" | "include" | "ignore" | "maxFileSize" | "noDefaultIgnores" | "scanMinified" | "baseline";
|
|
87
|
+
/** A successful parse: resolved options plus which configurable keys were explicit. */
|
|
88
|
+
export interface ParsedRun {
|
|
89
|
+
kind: "run";
|
|
90
|
+
options: QscanOptions;
|
|
91
|
+
/** The set of {@link ConfigurableKey}s the user set via a flag. */
|
|
92
|
+
explicit: Set<ConfigurableKey>;
|
|
93
|
+
}
|
|
94
|
+
/** Result of {@link parseArgs}: either resolved options or a meta action. */
|
|
95
|
+
export type ParsedArgs = ParsedRun | {
|
|
96
|
+
kind: "help";
|
|
97
|
+
} | {
|
|
98
|
+
kind: "version";
|
|
99
|
+
};
|
|
100
|
+
/** Thrown on malformed input; the CLI maps this to exit code 2. */
|
|
101
|
+
export declare class ArgError extends Error {
|
|
102
|
+
readonly name = "ArgError";
|
|
103
|
+
}
|
|
104
|
+
/** Default options, before any flags are applied. */
|
|
105
|
+
export declare function defaultOptions(): QscanOptions;
|
|
106
|
+
/**
|
|
107
|
+
* Parse a raw argv slice (i.e. without `node` and the script path).
|
|
108
|
+
*
|
|
109
|
+
* @throws {ArgError} On unknown flags, missing values, or invalid enum values.
|
|
110
|
+
*/
|
|
111
|
+
export declare function parseArgs(argv: readonly string[]): ParsedArgs;
|
|
112
|
+
/** Validate/normalize a `--format` value. */
|
|
113
|
+
export declare function asFormat(value: string): QscanFormat;
|
|
114
|
+
/** Validate/normalize a non-negative integer flag value. */
|
|
115
|
+
export declare function asInt(value: string, flag: string): number;
|
|
116
|
+
/** Validate/normalize a severity value. */
|
|
117
|
+
export declare function asSeverity(value: string): Severity;
|
|
118
|
+
/**
|
|
119
|
+
* Rank a severity: lower number = more severe.
|
|
120
|
+
* `critical` → 0, `info` → 4.
|
|
121
|
+
*/
|
|
122
|
+
export declare function severityRank(severity: Severity): number;
|
|
123
|
+
/**
|
|
124
|
+
* Whether `severity` is at or above `threshold` in seriousness.
|
|
125
|
+
* E.g. `high` meets a `medium` threshold; `low` does not.
|
|
126
|
+
*/
|
|
127
|
+
export declare function meetsThreshold(severity: Severity, threshold: Severity): boolean;
|
|
128
|
+
//# sourceMappingURL=args.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
|
package/dist/args.js
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zero-dependency command-line argument parsing for qScan.
|
|
3
|
+
*
|
|
4
|
+
* Hand-rolled rather than pulled from a library to keep the package free of
|
|
5
|
+
* runtime dependencies. The grammar is deliberately small: long flags
|
|
6
|
+
* (`--flag`, `--flag value`, `--flag=value`), a couple of short aliases
|
|
7
|
+
* (`-o`, `-v`, `-h`), repeatable `--ignore`, and a single optional positional
|
|
8
|
+
* path. Unknown flags are a usage error.
|
|
9
|
+
*/
|
|
10
|
+
/** Severities ordered most → least severe; index 0 is the most severe. */
|
|
11
|
+
export const SEVERITY_ORDER = ["critical", "high", "medium", "low", "info"];
|
|
12
|
+
const FORMATS = ["human", "json", "sarif", "cbom"];
|
|
13
|
+
/** Thrown on malformed input; the CLI maps this to exit code 2. */
|
|
14
|
+
export class ArgError extends Error {
|
|
15
|
+
name = "ArgError";
|
|
16
|
+
}
|
|
17
|
+
/** Default options, before any flags are applied. */
|
|
18
|
+
export function defaultOptions() {
|
|
19
|
+
return {
|
|
20
|
+
path: ".",
|
|
21
|
+
format: "human",
|
|
22
|
+
severityThreshold: "high",
|
|
23
|
+
source: true,
|
|
24
|
+
dependencies: true,
|
|
25
|
+
config: true,
|
|
26
|
+
ignore: [],
|
|
27
|
+
include: [],
|
|
28
|
+
noDefaultIgnores: false,
|
|
29
|
+
scanMinified: false,
|
|
30
|
+
changed: false,
|
|
31
|
+
parallel: false,
|
|
32
|
+
quiet: false,
|
|
33
|
+
noConfigFile: false,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Parse a raw argv slice (i.e. without `node` and the script path).
|
|
38
|
+
*
|
|
39
|
+
* @throws {ArgError} On unknown flags, missing values, or invalid enum values.
|
|
40
|
+
*/
|
|
41
|
+
export function parseArgs(argv) {
|
|
42
|
+
const options = defaultOptions();
|
|
43
|
+
const explicit = new Set();
|
|
44
|
+
let positional;
|
|
45
|
+
// Manual index walk so flags can consume the following token as a value.
|
|
46
|
+
for (let i = 0; i < argv.length; i++) {
|
|
47
|
+
const arg = argv[i];
|
|
48
|
+
// `--flag=value` → split into flag + inline value.
|
|
49
|
+
let inlineValue;
|
|
50
|
+
let flag = arg;
|
|
51
|
+
if (arg.startsWith("--") && arg.includes("=")) {
|
|
52
|
+
const eq = arg.indexOf("=");
|
|
53
|
+
flag = arg.slice(0, eq);
|
|
54
|
+
inlineValue = arg.slice(eq + 1);
|
|
55
|
+
}
|
|
56
|
+
/** Consume a value for `flag`: prefer the inline `=value`, else the next token. */
|
|
57
|
+
const takeValue = () => {
|
|
58
|
+
if (inlineValue !== undefined)
|
|
59
|
+
return inlineValue;
|
|
60
|
+
const next = argv[i + 1];
|
|
61
|
+
if (next === undefined || (next.startsWith("-") && next !== "-")) {
|
|
62
|
+
throw new ArgError(`option "${flag}" requires a value`);
|
|
63
|
+
}
|
|
64
|
+
i++;
|
|
65
|
+
return next;
|
|
66
|
+
};
|
|
67
|
+
switch (flag) {
|
|
68
|
+
case "-h":
|
|
69
|
+
case "--help":
|
|
70
|
+
return { kind: "help" };
|
|
71
|
+
case "-v":
|
|
72
|
+
case "--version":
|
|
73
|
+
return { kind: "version" };
|
|
74
|
+
case "--format":
|
|
75
|
+
options.format = asFormat(takeValue());
|
|
76
|
+
break;
|
|
77
|
+
case "--cbom":
|
|
78
|
+
options.format = "cbom";
|
|
79
|
+
break;
|
|
80
|
+
case "-o":
|
|
81
|
+
case "--output":
|
|
82
|
+
options.output = takeValue();
|
|
83
|
+
break;
|
|
84
|
+
case "--severity-threshold":
|
|
85
|
+
options.severityThreshold = asSeverity(takeValue());
|
|
86
|
+
explicit.add("severityThreshold");
|
|
87
|
+
break;
|
|
88
|
+
case "--no-source":
|
|
89
|
+
options.source = false;
|
|
90
|
+
explicit.add("source");
|
|
91
|
+
break;
|
|
92
|
+
case "--no-deps":
|
|
93
|
+
options.dependencies = false;
|
|
94
|
+
explicit.add("dependencies");
|
|
95
|
+
break;
|
|
96
|
+
case "--no-config":
|
|
97
|
+
options.config = false;
|
|
98
|
+
explicit.add("config");
|
|
99
|
+
break;
|
|
100
|
+
case "--ignore":
|
|
101
|
+
options.ignore.push(takeValue());
|
|
102
|
+
explicit.add("ignore");
|
|
103
|
+
break;
|
|
104
|
+
case "--include":
|
|
105
|
+
options.include.push(takeValue());
|
|
106
|
+
explicit.add("include");
|
|
107
|
+
break;
|
|
108
|
+
case "--max-file-size":
|
|
109
|
+
options.maxFileSize = asInt(takeValue(), "--max-file-size");
|
|
110
|
+
explicit.add("maxFileSize");
|
|
111
|
+
break;
|
|
112
|
+
case "--no-default-ignores":
|
|
113
|
+
options.noDefaultIgnores = true;
|
|
114
|
+
explicit.add("noDefaultIgnores");
|
|
115
|
+
break;
|
|
116
|
+
case "--scan-minified":
|
|
117
|
+
options.scanMinified = true;
|
|
118
|
+
explicit.add("scanMinified");
|
|
119
|
+
break;
|
|
120
|
+
// `quantakrypto.config.json` FILE controls (distinct from `--no-config`, which
|
|
121
|
+
// toggles config/TLS *detector* scanning above).
|
|
122
|
+
case "--config":
|
|
123
|
+
options.configFile = takeValue();
|
|
124
|
+
break;
|
|
125
|
+
case "--no-config-file":
|
|
126
|
+
options.noConfigFile = true;
|
|
127
|
+
break;
|
|
128
|
+
case "--changed":
|
|
129
|
+
options.changed = true;
|
|
130
|
+
break;
|
|
131
|
+
case "--since":
|
|
132
|
+
options.since = takeValue();
|
|
133
|
+
options.changed = true; // --since implies incremental mode
|
|
134
|
+
break;
|
|
135
|
+
case "--parallel":
|
|
136
|
+
options.parallel = true;
|
|
137
|
+
break;
|
|
138
|
+
case "--concurrency":
|
|
139
|
+
options.concurrency = asInt(takeValue(), "--concurrency");
|
|
140
|
+
options.parallel = true; // an explicit worker count implies parallel
|
|
141
|
+
break;
|
|
142
|
+
case "--baseline":
|
|
143
|
+
options.baseline = takeValue();
|
|
144
|
+
explicit.add("baseline");
|
|
145
|
+
break;
|
|
146
|
+
case "--write-baseline":
|
|
147
|
+
options.writeBaseline = takeValue();
|
|
148
|
+
break;
|
|
149
|
+
case "--quiet":
|
|
150
|
+
options.quiet = true;
|
|
151
|
+
break;
|
|
152
|
+
default:
|
|
153
|
+
if (flag.startsWith("-") && flag !== "-") {
|
|
154
|
+
throw new ArgError(`unknown option "${flag}"`);
|
|
155
|
+
}
|
|
156
|
+
if (positional !== undefined) {
|
|
157
|
+
throw new ArgError(`unexpected extra argument "${arg}" (path already set to "${positional}")`);
|
|
158
|
+
}
|
|
159
|
+
positional = arg;
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (positional !== undefined)
|
|
164
|
+
options.path = positional;
|
|
165
|
+
return { kind: "run", options, explicit };
|
|
166
|
+
}
|
|
167
|
+
/** Validate/normalize a `--format` value. */
|
|
168
|
+
export function asFormat(value) {
|
|
169
|
+
if (FORMATS.includes(value))
|
|
170
|
+
return value;
|
|
171
|
+
throw new ArgError(`invalid --format "${value}" (expected one of: ${FORMATS.join(", ")})`);
|
|
172
|
+
}
|
|
173
|
+
/** Validate/normalize a non-negative integer flag value. */
|
|
174
|
+
export function asInt(value, flag) {
|
|
175
|
+
if (!/^\d+$/.test(value)) {
|
|
176
|
+
throw new ArgError(`invalid ${flag} "${value}" (expected a non-negative integer)`);
|
|
177
|
+
}
|
|
178
|
+
return Number.parseInt(value, 10);
|
|
179
|
+
}
|
|
180
|
+
/** Validate/normalize a severity value. */
|
|
181
|
+
export function asSeverity(value) {
|
|
182
|
+
if (SEVERITY_ORDER.includes(value))
|
|
183
|
+
return value;
|
|
184
|
+
throw new ArgError(`invalid severity "${value}" (expected one of: ${SEVERITY_ORDER.join(", ")})`);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Rank a severity: lower number = more severe.
|
|
188
|
+
* `critical` → 0, `info` → 4.
|
|
189
|
+
*/
|
|
190
|
+
export function severityRank(severity) {
|
|
191
|
+
return SEVERITY_ORDER.indexOf(severity);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Whether `severity` is at or above `threshold` in seriousness.
|
|
195
|
+
* E.g. `high` meets a `medium` threshold; `low` does not.
|
|
196
|
+
*/
|
|
197
|
+
export function meetsThreshold(severity, threshold) {
|
|
198
|
+
return severityRank(severity) <= severityRank(threshold);
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=args.js.map
|
package/dist/args.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Baseline support for qScan — a thin adapter over the **canonical** baseline
|
|
3
|
+
* implementation in `@quantakrypto/core` (P1-1).
|
|
4
|
+
*
|
|
5
|
+
* Historically qScan carried its own baseline scheme (12-char hash of
|
|
6
|
+
* `ruleId|file|snippet|line`) that was incompatible with the GitHub Action's.
|
|
7
|
+
* Both have been unified onto core's single source of truth, so this module no
|
|
8
|
+
* longer defines its own format or hashing — it re-exports core's primitives
|
|
9
|
+
* and adds only the small filename-resolution / API-shape conveniences the CLI
|
|
10
|
+
* and `@quantakrypto/action` rely on.
|
|
11
|
+
*
|
|
12
|
+
* The on-disk format is core's {@link Baseline}: `{ version, fingerprints }`,
|
|
13
|
+
* where each fingerprint is a full, line-INSENSITIVE SHA-256 (so unrelated edits
|
|
14
|
+
* that shift line numbers no longer invalidate a baseline).
|
|
15
|
+
*/
|
|
16
|
+
import { baselineFromFindings, BASELINE_VERSION, fingerprintFinding, loadBaseline, saveBaseline } from "@quantakrypto/core";
|
|
17
|
+
import type { Baseline, Finding } from "@quantakrypto/core";
|
|
18
|
+
export { baselineFromFindings, BASELINE_VERSION, fingerprintFinding, loadBaseline, saveBaseline };
|
|
19
|
+
export type { Baseline };
|
|
20
|
+
/**
|
|
21
|
+
* Compute a stable fingerprint for a finding. Alias for core's
|
|
22
|
+
* {@link fingerprintFinding}; kept under the historical name `fingerprint` for
|
|
23
|
+
* source compatibility with existing call sites and tests.
|
|
24
|
+
*/
|
|
25
|
+
export declare const fingerprint: typeof fingerprintFinding;
|
|
26
|
+
/**
|
|
27
|
+
* Partition findings into those kept and those suppressed by a baseline.
|
|
28
|
+
*
|
|
29
|
+
* Wraps core's {@link coreApplyBaseline} (which takes a {@link Baseline} and
|
|
30
|
+
* returns `{ newFindings, suppressed }`) but preserves qScan's historical
|
|
31
|
+
* `{ kept, suppressed }` shape and its lenient `ReadonlySet<string>` overload so
|
|
32
|
+
* existing callers keep working.
|
|
33
|
+
*
|
|
34
|
+
* @param findings All findings produced by a scan.
|
|
35
|
+
* @param baseline Either a {@link Baseline} object or a set of accepted
|
|
36
|
+
* fingerprints.
|
|
37
|
+
*/
|
|
38
|
+
export declare function applyBaseline(findings: readonly Finding[], baseline: Baseline | ReadonlySet<string>): {
|
|
39
|
+
kept: Finding[];
|
|
40
|
+
suppressed: Finding[];
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Build a {@link Baseline} from a set of findings (deduped + sorted). Alias for
|
|
44
|
+
* core's {@link baselineFromFindings} under qScan's historical name.
|
|
45
|
+
*/
|
|
46
|
+
export declare function buildBaseline(findings: readonly Finding[]): Baseline;
|
|
47
|
+
/**
|
|
48
|
+
* Read a baseline file from disk and return its accepted fingerprints as a set.
|
|
49
|
+
*
|
|
50
|
+
* Unlike core's tolerant {@link loadBaseline} (which returns an empty baseline
|
|
51
|
+
* for a missing/unparseable file), this preserves qScan's historical *strict*
|
|
52
|
+
* contract: a missing or malformed file is an error, surfaced to the CLI as an
|
|
53
|
+
* I/O failure (exit 2).
|
|
54
|
+
*
|
|
55
|
+
* @throws {Error} If the file cannot be read or is not valid baseline JSON.
|
|
56
|
+
*/
|
|
57
|
+
export declare function readBaseline(path: string): Promise<Set<string>>;
|
|
58
|
+
/** Serialize and write a baseline to disk (pretty-printed, trailing newline). */
|
|
59
|
+
export declare function writeBaseline(path: string, baseline: Baseline): Promise<void>;
|
|
60
|
+
//# sourceMappingURL=baseline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"baseline.d.ts","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAEL,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,EACZ,YAAY,EACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAI5D,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC;AAClG,YAAY,EAAE,QAAQ,EAAE,CAAC;AAEzB;;;;GAIG;AACH,eAAO,MAAM,WAAW,2BAAqB,CAAC;AAE9C;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,SAAS,OAAO,EAAE,EAC5B,QAAQ,EAAE,QAAQ,GAAG,WAAW,CAAC,MAAM,CAAC,GACvC;IAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IAAC,UAAU,EAAE,OAAO,EAAE,CAAA;CAAE,CAO5C;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,QAAQ,CAEpE;AAED;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAoBrE;AAED,iFAAiF;AACjF,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAQnF"}
|