mjolnir-qa 0.5.3 → 0.5.5
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/CHANGELOG.md +143 -0
- package/README.md +11 -4
- package/dist/cli.d.mts +158 -39
- package/dist/cli.mjs +7694 -6010
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,147 @@ Rule behavior changes (new rules, FP-rate changes against the corpus,
|
|
|
9
9
|
severity changes) are first-class entries here — rule IDs are immutable
|
|
10
10
|
once shipped, so this file is the record of what changed between versions.
|
|
11
11
|
|
|
12
|
+
# [0.5.4] — 2026-09-06
|
|
13
|
+
|
|
14
|
+
### Agent Handoff + Minimized Reporting (plan 1788599400000)
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **`mjolnir why <file>:<line>`** — occurrence-level evidence query
|
|
19
|
+
(informational, NOT a gate): exact file+line match, severity icon,
|
|
20
|
+
message/why/fix, evidence level, trust level, measured FP rate
|
|
21
|
+
(or the honest "ships on assumption"), runtime corroboration when
|
|
22
|
+
present, and the suppression contract (reason required, 90-day
|
|
23
|
+
expiry). Saved-report mode (`--json <mjolnir.json>`) is
|
|
24
|
+
authoritative; live scan runs otherwise. Exit 0 match / 1 no match.
|
|
25
|
+
- **`mjolnir handoff [mjolnir.json]`** — the deterministic fix-handoff
|
|
26
|
+
artifact: per-rule remediation sections (what is wrong / why
|
|
27
|
+
Mjölnir believes it / evidence boundary by level / occurrences
|
|
28
|
+
capped at 25 / fix / constraints / occurrences list), a per-rule
|
|
29
|
+
fenced copy block and a one-shot handoff prompt, and the formal
|
|
30
|
+
verification contract (TARGET_RESOLVED / TARGET_REMAINS /
|
|
31
|
+
NEW_FINDINGS_INTRODUCED / VERIFICATION_NOT_RUN, correlated by the
|
|
32
|
+
fingerprint ruleId+file+message; the standing caveat that a clean
|
|
33
|
+
`--scope changed` run verifies the changed surface only). Generated
|
|
34
|
+
solely from Mjölnir's own rule metadata — offline, deterministic,
|
|
35
|
+
escapeMarkdown'd. Zero findings → exit 0, non-actionable clean
|
|
36
|
+
artifact with no prompt. `--category`/`--rules` are presentation
|
|
37
|
+
filters.
|
|
38
|
+
- **`mjolnir install`** — installs the agent instruction surfaces
|
|
39
|
+
(`.claude/commands/mjolnir.md`, `.kilo/command/mjolnir.md`,
|
|
40
|
+
`.cursor/rules/mjolnir.mdc`, marker-appended `AGENTS.md`): the
|
|
41
|
+
version-pinned trust loop brief (scan `--scope changed` before
|
|
42
|
+
finishing, never suppress to green, report files changed and checks
|
|
43
|
+
not run). `--staged-hook` adds a NON-BLOCKING pre-commit hook
|
|
44
|
+
(`mjolnir --staged --blocking warning`, reusing `.husky`/
|
|
45
|
+
`core.hooksPath` when present). Marker-based idempotency;
|
|
46
|
+
`--dry-run` writes nothing; refusal (exit 10) before overwriting
|
|
47
|
+
any non-Mjölnir file; `--force` overwrites only Mjölnir-marked
|
|
48
|
+
files; never @latest.
|
|
49
|
+
- **`--score`** — prints only the numeric score (`unknown` when no
|
|
50
|
+
tests exist — never a fake 0); pure rendering flag, exit code
|
|
51
|
+
unchanged; stderr note when --json was also requested.
|
|
52
|
+
- **`--category <cat>`** (repeatable) — presentation filter on the
|
|
53
|
+
terminal findings display (and handoff/why): NEVER filters the
|
|
54
|
+
scan, the JSON/SARIF output, or the score; the terminal prints
|
|
55
|
+
`filtered view: N of M findings shown; score reflects the full
|
|
56
|
+
scan`. Unknown categories are a usage error (exit 10).
|
|
57
|
+
- **`--staged`** — scan-surface restriction: intersects discovered
|
|
58
|
+
test files with the git staged list; score reflects the staged
|
|
59
|
+
surface and is labeled as such (`staged surface: N file(s)`); not a
|
|
60
|
+
git repo → honest degraded fallback; empty staged set → exit 0.
|
|
61
|
+
- **`--blocking error|warning|none`** — exit-status override only:
|
|
62
|
+
maps onto the existing gate model (none→advisory, error→errors
|
|
63
|
+
block, warning→errors+warnings block). Detection and rendering are
|
|
64
|
+
identical under all three values; E0 findings never block; partial
|
|
65
|
+
scans stay exit 2.
|
|
66
|
+
- **`fixGroupId`** (additive JSON field): the stable semantic identity
|
|
67
|
+
of a remediation group — intentionally distinct from `ruleId`
|
|
68
|
+
(which identifies the detector). Current strategy: one rule = one
|
|
69
|
+
group, so fixGroupId equals ruleId today; consumers must not rely
|
|
70
|
+
on that permanently.
|
|
71
|
+
|
|
72
|
+
### Changed
|
|
73
|
+
|
|
74
|
+
- help registry gained `why`, `handoff`, `install` and the new flags;
|
|
75
|
+
site/reference/cli.md documents the handoff trust model.
|
|
76
|
+
|
|
77
|
+
## [0.5.5] — 2026-09-06
|
|
78
|
+
|
|
79
|
+
### Audit Remediation 1.0 (engine correctness + trust boundary)
|
|
80
|
+
|
|
81
|
+
### Changed — plugin execution gate (audit C2, contract-visible, pre-1.0)
|
|
82
|
+
|
|
83
|
+
- **`--enable-plugins` / `MJOLNIR_ENABLE_PLUGINS=1`** — npm plugins and
|
|
84
|
+
JS-module external rules (`mjolnir-rules/*.mjs`) now load and execute
|
|
85
|
+
only behind an explicit opt-in (default OFF). Declared-but-gated
|
|
86
|
+
sources are listed on a loud stderr notice; they are never imported.
|
|
87
|
+
JSON rule manifests execute no code and load without the gate. See
|
|
88
|
+
SECURITY.md (trust model) and docs/VERSIONING.md (gate contract).
|
|
89
|
+
|
|
90
|
+
### Fixed — engine correctness (audit M1)
|
|
91
|
+
|
|
92
|
+
- **C1/W9 (`--cache`)**: the cache key folds in the repo-relative path,
|
|
93
|
+
adapter id, and parse mode — byte-identical files no longer share
|
|
94
|
+
verdicts (findings carried the first-scanned file's path), and
|
|
95
|
+
regex-fallback verdicts are never served as AST ones. `CACHE_VERSION`
|
|
96
|
+
bumped to 2: one-time invalidation, first post-upgrade scan cold.
|
|
97
|
+
- **C3**: the default console sinks are variadic —
|
|
98
|
+
"mjolnir internal error:" now carries the actual cause (the message
|
|
99
|
+
used to be dropped).
|
|
100
|
+
- **C5**: a partial (truncated) scan never writes the
|
|
101
|
+
first-clean-scan milestone; `mjolnir diff` on a truncated head
|
|
102
|
+
returns exit 2 and folds no resolved findings into stats.
|
|
103
|
+
- **W1**: the Java/C# maskers keep code after a closed block comment
|
|
104
|
+
live (`/*x*/y` used to blank `y`).
|
|
105
|
+
- **W2/W3**: parse-semaphore re-check (cap holds under fan-out) and
|
|
106
|
+
parser-creation retry on rejection (a transient WASM load failure no
|
|
107
|
+
longer disables the AST path for the process).
|
|
108
|
+
- **W4**: unchecked grammar-shape casts removed in the QA-model
|
|
109
|
+
extractor; `parseTsFile`'s undefined contract honored.
|
|
110
|
+
- **W8**: runtime corroboration claims "test" level only when the
|
|
111
|
+
finding's line falls inside the verdict's span; lineless reports
|
|
112
|
+
degrade to file level.
|
|
113
|
+
- **W10**: malformed rule records at the rule→Finding boundary are
|
|
114
|
+
rejected with a diagnostic (crash channel), never silently scored.
|
|
115
|
+
- **S5 (QA-CI-001, detectorRevision 2)**: step-level line resolution
|
|
116
|
+
falls back to the anchor line instead of crashing — the rule no
|
|
117
|
+
longer vanishes into crash isolation on workflows where the raw
|
|
118
|
+
`continue-on-error: true` literal sits outside the search window.
|
|
119
|
+
Behavior changes from crash-dropped to reported; sidecar +
|
|
120
|
+
measured-FP regenerated per policy.
|
|
121
|
+
- **masking.ts**: outer-delimiter search fixes misclassification of
|
|
122
|
+
string values ending in a nested quote.
|
|
123
|
+
- **changed.ts**: hunk-content lines that look like diff headers no
|
|
124
|
+
longer abandon the hunk; one unreadable untracked file degrades only
|
|
125
|
+
itself (treated fully changed), never the whole scope.
|
|
126
|
+
- **shared-walk.ts**: unreadable directories and skipped symlinks are
|
|
127
|
+
counted skips with reasons; deadline/cap checks run inside the entry
|
|
128
|
+
loop.
|
|
129
|
+
- **workflow-parser.ts**: the documented nesting-depth cap is enforced;
|
|
130
|
+
`jobs` builds on a null-prototype object; step `with` is copied.
|
|
131
|
+
|
|
132
|
+
### Changed — suppressions expiry (audit S4, contract-visible, pre-1.0)
|
|
133
|
+
|
|
134
|
+
- The 90-day default for hand-authored `ignore` entries is no longer
|
|
135
|
+
anchored at the config file's mtime — any config edit used to reset
|
|
136
|
+
every suppression window. Expiry is the entry's explicit `expires`
|
|
137
|
+
date; entries without one stay active and are labeled accordingly in
|
|
138
|
+
`mjolnir suppressions`.
|
|
139
|
+
|
|
140
|
+
### Hardened — trust boundary (audits S1/S2/S3/S7)
|
|
141
|
+
|
|
142
|
+
- **S1**: git resolves to an absolute path from PATH (never the scanned
|
|
143
|
+
CWD) — a planted `git.exe`/`git.bat` cannot hijack Mjölnir's git
|
|
144
|
+
calls on Windows.
|
|
145
|
+
- **S2**: ignore/glob patterns and external JSON-rule regexes are
|
|
146
|
+
length/wildcard-capped at compile time; `**/` compiles segment-aware.
|
|
147
|
+
- **S3**: config/ignore/plugins resolve from the explicit scan target's
|
|
148
|
+
project; the resolved anchor prints in verbose mode.
|
|
149
|
+
- **S7**: `ignore[].files` must be `string[]` (exit 10 on typo);
|
|
150
|
+
unknown top-level config keys warn; baseline `schemaVersion` is
|
|
151
|
+
checked (future versions degrade to "no baseline" with a warning).
|
|
152
|
+
|
|
12
153
|
## [0.5.3] — 2026-09-05
|
|
13
154
|
|
|
14
155
|
### Terminal + CI UX Overhaul (plan 1788579907109)
|
|
@@ -159,6 +300,8 @@ rules… → Scoring…`) fed by the new additive `ScanHooks.onProgress`.
|
|
|
159
300
|
|
|
160
301
|
### Added — folder-based external rules, zero network
|
|
161
302
|
|
|
303
|
+
### Added — folder-based external rules, zero network
|
|
304
|
+
|
|
162
305
|
- **`mjolnir-rules/` contract** (`src/plugins/local-rules.ts`): a
|
|
163
306
|
workspace directory loaded from the scan target root alongside npm
|
|
164
307
|
plugins. Two file kinds: **JSON rule manifests** (declarative regex
|
package/README.md
CHANGED
|
@@ -558,10 +558,17 @@ are immutable once shipped and never reused.
|
|
|
558
558
|
real OSS code ship in the headline tiers (see
|
|
559
559
|
[How much of this is measured](#how-much-of-this-is-measured)); the scan
|
|
560
560
|
footer and `mjolnir rules --unmeasured` tell you which is which.
|
|
561
|
-
- **Plugin trust** — plugins are npm packages declared
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
561
|
+
- **Plugin trust & execution gate** — plugins are npm packages declared
|
|
562
|
+
under `"plugins"`; JS modules live in `mjolnir-rules/*.mjs`. There is
|
|
563
|
+
**no sandbox**: plugin code runs with full Node privileges, the same
|
|
564
|
+
trust model as ESLint or Vitest plugins. Because of that, code execution
|
|
565
|
+
is **opt-in at every scan**: pass `--enable-plugins` (or set
|
|
566
|
+
`MJOLNIR_ENABLE_PLUGINS=1`) or the sources are NOT loaded — a loud
|
|
567
|
+
stderr notice lists exactly what was skipped. Scanning untrusted code
|
|
568
|
+
never executes it. JSON rule manifests (`mjolnir-rules/*.json`) are
|
|
569
|
+
unaffected: they declare regex patterns and execute no code by design.
|
|
570
|
+
Core rule-ID prefixes are reserved and rejected from plugins and
|
|
571
|
+
external rules to prevent spoofing.
|
|
565
572
|
- **Workspace-local external rules** (folder-based, zero network) — a
|
|
566
573
|
`mjolnir-rules/` directory next to the scan target loads custom rules:
|
|
567
574
|
JSON files declare regex patterns (no code executed), `.mjs`/`.js`
|
package/dist/cli.d.mts
CHANGED
|
@@ -33,7 +33,7 @@ type EvidenceLevel = (typeof EVIDENCE_ORDER)[number];
|
|
|
33
33
|
*/
|
|
34
34
|
type QaImpact = "BLOCKS-RELEASE" | "FLAKY-RISK" | "FALSE-GREEN" | "HYGIENE";
|
|
35
35
|
/** Rule namespaces are frozen public API (§18.4). IDs are never reused. */
|
|
36
|
-
type RuleCategory = "QA-TEST" | "QA-TQUAL" | "QA-PW" | "QA-CI";
|
|
36
|
+
type RuleCategory = "QA-TEST" | "QA-TQUAL" | "QA-PW" | "QA-CI" | "QA-PY" | "QA-ENV" | "QA-JV" | "QA-CS" | "QA-CYP" | "QA-SE" | "QA-WDIO" | "QA-PPTR" | "QA-APM";
|
|
37
37
|
/**
|
|
38
38
|
* Trust levels (Verification Trust Evolution Plan §16): the OVERALL
|
|
39
39
|
* trust a consumer can place in one finding, combining the static
|
|
@@ -128,6 +128,21 @@ interface Finding {
|
|
|
128
128
|
/** How to fix — concrete action. */
|
|
129
129
|
fix: string;
|
|
130
130
|
docsUrl?: string;
|
|
131
|
+
/**
|
|
132
|
+
* Stable semantic identity of a remediation group: findings that can
|
|
133
|
+
* be reasoned about and potentially remediated as one root-cause unit
|
|
134
|
+
* (agent-handoff plan §5.1). This is intentionally a DIFFERENT
|
|
135
|
+
* concept from `ruleId` (which identifies the detector/rule).
|
|
136
|
+
*
|
|
137
|
+
* Current implementation strategy: fixGroupId = ruleId, because each
|
|
138
|
+
* rule currently represents one remediation group. Future rules may
|
|
139
|
+
* emit multiple findings belonging to one fix group, or multiple
|
|
140
|
+
* remediation groups. Consumers MUST NOT rely on
|
|
141
|
+
* fixGroupId === ruleId permanently.
|
|
142
|
+
*
|
|
143
|
+
* Additive within schemaVersion 1.
|
|
144
|
+
*/
|
|
145
|
+
fixGroupId?: string;
|
|
131
146
|
}
|
|
132
147
|
interface DimensionScore {
|
|
133
148
|
category: RuleCategory;
|
|
@@ -147,6 +162,16 @@ interface ScanResult {
|
|
|
147
162
|
/** Present when --scope changed was requested. */
|
|
148
163
|
scope?: "all" | "changed";
|
|
149
164
|
scopeDegraded?: string;
|
|
165
|
+
/**
|
|
166
|
+
* Present when --staged was requested (agent-handoff plan §5.7):
|
|
167
|
+
* the scan surface was restricted to git staged files, and `files`
|
|
168
|
+
* is how many survived the intersection. The score reflects THAT
|
|
169
|
+
* surface — never present it as a full-repo score. Additive within
|
|
170
|
+
* schemaVersion 1.
|
|
171
|
+
*/
|
|
172
|
+
staged?: {
|
|
173
|
+
files: number;
|
|
174
|
+
};
|
|
150
175
|
/** Detected test frameworks (0.2). Empty + unknown=true when undetectable. */
|
|
151
176
|
frameworks: string[];
|
|
152
177
|
frameworkDetectionUnknown: boolean;
|
|
@@ -223,6 +248,20 @@ interface ScanResult {
|
|
|
223
248
|
};
|
|
224
249
|
}
|
|
225
250
|
//#endregion
|
|
251
|
+
//#region src/engine/tier-policy.d.ts
|
|
252
|
+
type Tier = "core" | "extended" | "quarantine";
|
|
253
|
+
//#endregion
|
|
254
|
+
//#region src/engine/overlap-dedup.d.ts
|
|
255
|
+
/** Per-rule metadata the dedup pass needs, built from RULES. */
|
|
256
|
+
interface OverlapMeta {
|
|
257
|
+
/** Rule IDs this rule can double-report on the same root cause. */
|
|
258
|
+
overlapWith?: string[];
|
|
259
|
+
/** Tier rank for survivor resolution (missing = core). */
|
|
260
|
+
tier?: Tier;
|
|
261
|
+
/** RULES registry order for final tie-breaking. */
|
|
262
|
+
order?: number;
|
|
263
|
+
}
|
|
264
|
+
//#endregion
|
|
226
265
|
//#region src/discovery/workspace.d.ts
|
|
227
266
|
/**
|
|
228
267
|
* Repository discovery (Sprint-Plan W1-03).
|
|
@@ -238,9 +277,6 @@ interface Workspace {
|
|
|
238
277
|
workspaceGlobs: string[];
|
|
239
278
|
}
|
|
240
279
|
//#endregion
|
|
241
|
-
//#region src/engine/tier-policy.d.ts
|
|
242
|
-
type Tier = "core" | "extended" | "quarantine";
|
|
243
|
-
//#endregion
|
|
244
280
|
//#region src/rules/rule.d.ts
|
|
245
281
|
/**
|
|
246
282
|
* How the rule's primary detection decision is made (Verification Trust
|
|
@@ -480,19 +516,13 @@ interface UniversalRule {
|
|
|
480
516
|
run(file: ParsedFile): Array<Omit<Finding, "ruleId" | "category">>;
|
|
481
517
|
}
|
|
482
518
|
//#endregion
|
|
483
|
-
//#region src/
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
* discipline as SARIF's `driver.version` — kept in sync by
|
|
491
|
-
* `scripts/sync-sarif-version.cjs` on release and guarded by
|
|
492
|
-
* `tests/version-consistency.spec.ts` locally.
|
|
493
|
-
*/
|
|
494
|
-
declare const CLI_VERSION = "0.5.3";
|
|
495
|
-
declare function buildUniversalRules(root: string, strict?: boolean): Promise<{
|
|
519
|
+
//#region src/engine/scan-pipeline.d.ts
|
|
520
|
+
declare function buildUniversalRules$1(root: string, strict?: boolean, opts?: {
|
|
521
|
+
/** CLI trust-gate flag (`--enable-plugins`) for this scan. */
|
|
522
|
+
enablePlugins?: boolean;
|
|
523
|
+
/** Called with the gate notice when code sources were skipped. */
|
|
524
|
+
onGateNotice?: (notice: string) => void;
|
|
525
|
+
}): Promise<{
|
|
496
526
|
rules: UniversalRule[];
|
|
497
527
|
pluginErrors: string[];
|
|
498
528
|
tierByRuleId: Map<string, Tier>;
|
|
@@ -536,25 +566,41 @@ interface CliArgs {
|
|
|
536
566
|
* and auto-disabled in CI/machine formats; this flag is the manual off.
|
|
537
567
|
*/
|
|
538
568
|
noProgress?: boolean;
|
|
569
|
+
/**
|
|
570
|
+
* --staged: scan-surface restriction (plan §5.7) — intersect the
|
|
571
|
+
* discovered test files with the git staged file list. Does NOT
|
|
572
|
+
* change rule semantics; score implications come only from the
|
|
573
|
+
* narrowed surface (labeled as such in the report).
|
|
574
|
+
*/
|
|
575
|
+
staged?: boolean;
|
|
576
|
+
/**
|
|
577
|
+
* --blocking error|warning|none (plan §5.8): invocation-level gate
|
|
578
|
+
* override. Controls PROCESS EXIT BEHAVIOR ONLY via the existing
|
|
579
|
+
* exitForFindings mechanism — detection, rendering, JSON and score
|
|
580
|
+
* are identical under all three values. Overrides config.gate.
|
|
581
|
+
*/
|
|
582
|
+
blocking?: "error" | "warning" | "none";
|
|
583
|
+
/**
|
|
584
|
+
* --category <cat> (repeatable): presentation filter — narrows the
|
|
585
|
+
* TERMINAL findings display (and handoff/why output) to the given
|
|
586
|
+
* rule categories. NEVER filters the scan, the JSON/SARIF output,
|
|
587
|
+
* or the score (agent-handoff plan §5.5).
|
|
588
|
+
*/
|
|
589
|
+
categories?: RuleCategory[];
|
|
590
|
+
/**
|
|
591
|
+
* --score: print only the numeric score (or `unknown` when the repo
|
|
592
|
+
* has no tests) instead of the full report. Pure rendering flag:
|
|
593
|
+
* scan semantics and exit codes are unchanged (plan §5.6).
|
|
594
|
+
*/
|
|
595
|
+
scoreOnly?: boolean;
|
|
596
|
+
/**
|
|
597
|
+
* Audit C2: --enable-plugins opens the plugin trust gate for THIS
|
|
598
|
+
* invocation — npm-plugin and JS-module rule sources may load (and
|
|
599
|
+
* execute). Default OFF; MJOLNIR_ENABLE_PLUGINS=1 is the env
|
|
600
|
+
* equivalent. JSON rule manifests are unaffected (no code by design).
|
|
601
|
+
*/
|
|
602
|
+
enablePlugins?: boolean;
|
|
539
603
|
}
|
|
540
|
-
/** A usage-error detail: the offending token, when one exists. */
|
|
541
|
-
interface UsageErrorDetail {
|
|
542
|
-
/** The unknown flag or rejected value (e.g. `--nope`, `loud`). */
|
|
543
|
-
token?: string | undefined;
|
|
544
|
-
/** The flag whose value was rejected (`--tone` for `--tone loud`). */
|
|
545
|
-
flag?: string | undefined;
|
|
546
|
-
}
|
|
547
|
-
declare function parseArgs(argv: string[], onError?: (detail: UsageErrorDetail) => void): CliArgs | null;
|
|
548
|
-
/** Hand-rolled Levenshtein distance (plan M2: no new dependencies). */
|
|
549
|
-
declare function levenshtein(a: string, b: string): number;
|
|
550
|
-
/** Nearest known flags within distance ≤ 2, nearest first. */
|
|
551
|
-
declare function nearestFlags(flag: string, max?: number): string[];
|
|
552
|
-
/**
|
|
553
|
-
* Friendly usage error (plan M2, exit 10 preserved): nearest-flag
|
|
554
|
-
* suggestion, the valid neighbors, and the exact help command. Printed
|
|
555
|
-
* to stderr; findings/usage stay on their documented streams.
|
|
556
|
-
*/
|
|
557
|
-
declare function usageErrorMessage(detail: UsageErrorDetail): string;
|
|
558
604
|
interface ScanHooks {
|
|
559
605
|
/** Invoked when a rule throws on a file (audit R-9). */
|
|
560
606
|
onRuleCrash?: (ruleId: string, file: string, error: unknown) => void;
|
|
@@ -572,6 +618,13 @@ interface ScanHooks {
|
|
|
572
618
|
total?: number | undefined;
|
|
573
619
|
detail?: string | undefined;
|
|
574
620
|
}) => void;
|
|
621
|
+
/**
|
|
622
|
+
* Audit C2: invoked when code-executing rule sources were skipped
|
|
623
|
+
* because the plugin trust gate is closed. Default (no hook): the
|
|
624
|
+
* gate notice is written straight to stderr — loud in every verb,
|
|
625
|
+
* never on the stdout machine contracts.
|
|
626
|
+
*/
|
|
627
|
+
onGateNotice?: (notice: string) => void;
|
|
575
628
|
}
|
|
576
629
|
/**
|
|
577
630
|
* Workspace fallback for targets with no discoverable project root
|
|
@@ -579,7 +632,40 @@ interface ScanHooks {
|
|
|
579
632
|
* root-path degenerate case (`C:\` → basename "") is testable without
|
|
580
633
|
* scanning a filesystem root.
|
|
581
634
|
*/
|
|
582
|
-
declare function fallbackWorkspace(targetAbs: string): Workspace;
|
|
635
|
+
declare function fallbackWorkspace$1(targetAbs: string): Workspace;
|
|
636
|
+
/**
|
|
637
|
+
* Minimal glob match for suppression `files` patterns, with gitignore
|
|
638
|
+
* `**` semantics (bug-audit M5). Supports:
|
|
639
|
+
* "tests/**" — everything inside tests/
|
|
640
|
+
* "tests" + "/**\/*.spec.ts" — any depth UNDER tests/ (including none) ending in .spec.ts
|
|
641
|
+
* "**" + "/*.spec.ts" — any depth including root-level files
|
|
642
|
+
* "tests/foo.spec.ts" — exact path
|
|
643
|
+
* "*" within a segment never crosses "/".
|
|
644
|
+
*
|
|
645
|
+
* Forward slashes only (findings always use normalized paths). `?`,
|
|
646
|
+
* character classes and `!` negation are not metacharacters here — same
|
|
647
|
+
* as before this rewrite.
|
|
648
|
+
*/
|
|
649
|
+
declare function pathMatchesGlob$1(path: string, glob: string): boolean;
|
|
650
|
+
/**
|
|
651
|
+
* Plan §16: locate a runtime run report next to the scan target, using
|
|
652
|
+
* the exact conventions the forensics ingestion already accepts —
|
|
653
|
+
* `mjolnir.report.json` (the packages/playwright-reporter default
|
|
654
|
+
* output) or a `test-results/` directory. Returns the path for
|
|
655
|
+
* `runForensics`, or undefined when neither convention is present
|
|
656
|
+
* ("no runtime evidence" — never guessed).
|
|
657
|
+
*/
|
|
658
|
+
declare function discoverRuntimeReport$1(scanRoot: string): string | undefined;
|
|
659
|
+
/**
|
|
660
|
+
* Audit W10 — runtime shape validation at the rule→Finding boundary.
|
|
661
|
+
* A finding record coming out of a rule (plugin/JSON-manifest rules are
|
|
662
|
+
* external data, not trusted internal code) must carry the fields the
|
|
663
|
+
* whole downstream pipeline indexes on: severity within the enum,
|
|
664
|
+
* integer line ≥ 1, non-empty message, and a file path. Malformed
|
|
665
|
+
* records are rejected (routed to the crash/plugin-error channel by the
|
|
666
|
+
* caller) — never silently scored.
|
|
667
|
+
*/
|
|
668
|
+
declare function isValidFindingRecord$1(f: unknown): f is Omit<Finding, "ruleId" | "category">;
|
|
583
669
|
/**
|
|
584
670
|
* Testable default scan path core. `hooks` lets callers observe
|
|
585
671
|
* normally-invisible events (swallowed rule crashes) without changing
|
|
@@ -590,9 +676,42 @@ declare function fallbackWorkspace(targetAbs: string): Workspace;
|
|
|
590
676
|
* inherently async); `runRules` and every rule stay synchronous and
|
|
591
677
|
* consume `ParsedFile.ast`. Callers await the returned promise.
|
|
592
678
|
*/
|
|
593
|
-
declare function runScan(args: CliArgs, hooks?: ScanHooks): Promise<ScanResult>;
|
|
679
|
+
declare function runScan$1(args: CliArgs, hooks?: ScanHooks): Promise<ScanResult>;
|
|
680
|
+
//#endregion
|
|
681
|
+
//#region src/cli.d.ts
|
|
682
|
+
declare const runScan: typeof runScan$1, buildUniversalRules: typeof buildUniversalRules$1, fallbackWorkspace: typeof fallbackWorkspace$1, pathMatchesGlob: typeof pathMatchesGlob$1, isValidFindingRecord: typeof isValidFindingRecord$1, discoverRuntimeReport: typeof discoverRuntimeReport$1, KNOWN_RULE_IDS: ReadonlySet<string>, OVERLAP_META_BY_RULE_ID: ReadonlyMap<string, OverlapMeta>, EVIDENCE_OVERRIDES: ReadonlyMap<string, string>, SUITE_INVALIDATING_RULE_IDS: ReadonlySet<string>;
|
|
683
|
+
/**
|
|
684
|
+
* Tool version for `mjolnir --version`.
|
|
685
|
+
*
|
|
686
|
+
* A literal, not a package.json read: the shipped artifact is a single
|
|
687
|
+
* bundled `dist/cli.mjs`, so resolving package.json at runtime depends on
|
|
688
|
+
* where the file happens to sit after install. This follows the same
|
|
689
|
+
* discipline as SARIF's `driver.version` — kept in sync by
|
|
690
|
+
* `scripts/sync-sarif-version.cjs` on release and guarded by
|
|
691
|
+
* `tests/version-consistency.spec.ts` locally.
|
|
692
|
+
*/
|
|
693
|
+
declare const CLI_VERSION = "0.5.5";
|
|
694
|
+
/** A usage-error detail: the offending token, when one exists. */
|
|
695
|
+
interface UsageErrorDetail {
|
|
696
|
+
/** The unknown flag or rejected value (e.g. `--nope`, `loud`). */
|
|
697
|
+
token?: string | undefined;
|
|
698
|
+
/** The flag whose value was rejected (`--tone` for `--tone loud`). */
|
|
699
|
+
flag?: string | undefined;
|
|
700
|
+
}
|
|
701
|
+
declare function parseArgs(argv: string[], onError?: (detail: UsageErrorDetail) => void): CliArgs | null;
|
|
702
|
+
/** Hand-rolled Levenshtein distance (plan M2: no new dependencies). */
|
|
703
|
+
declare function levenshtein(a: string, b: string): number;
|
|
704
|
+
/** Nearest known flags within distance ≤ 2, nearest first. */
|
|
705
|
+
declare function nearestFlags(flag: string, max?: number): string[];
|
|
706
|
+
/**
|
|
707
|
+
* Friendly usage error (plan M2, exit 10 preserved): nearest-flag
|
|
708
|
+
* suggestion, the valid neighbors, and the exact help command. Printed
|
|
709
|
+
* to stderr; findings/usage stay on their documented streams.
|
|
710
|
+
*/
|
|
711
|
+
declare function usageErrorMessage(detail: UsageErrorDetail): string;
|
|
594
712
|
type Output = (...parts: unknown[]) => void;
|
|
595
|
-
declare
|
|
713
|
+
declare const out: Output;
|
|
714
|
+
declare const err: Output;
|
|
596
715
|
/** Testable `ci install` handler. Returns the process exit code. */
|
|
597
716
|
declare function runCiInstall(argv: string[], io?: {
|
|
598
717
|
out: Output;
|
|
@@ -735,4 +854,4 @@ declare function runHelpCommand(argv: string[], io?: {
|
|
|
735
854
|
declare function internalErrorMessage(err: unknown, emit: (s: string) => void, debug: boolean): void;
|
|
736
855
|
declare function isEntryPoint(): boolean;
|
|
737
856
|
//#endregion
|
|
738
|
-
export { CLI_VERSION, Output, ScanHooks, UsageErrorDetail, buildUniversalRules, exitForFindings, fallbackWorkspace, internalErrorMessage, isEntryPoint, levenshtein, main, nearestFlags, parseArgs, pathMatchesGlob, runBadgeCommand, runBaselineCommand, runCiInstall, runCreateRuleCommand, runDebtCommand, runDiffCommand, runDoctorCommand, runDoctorPlaywright, runExplainCommand, runFixCommand, runForensicsCommand, runHandoverCommand, runHelpCommand, runImpactCommand, runInitCommand, runPrCommentCommand, runPwReportCommand, runRulesCommand, runScan, runScanCommand, runStatsCommand, runSuppressions, runTriageCommand, usageErrorMessage };
|
|
857
|
+
export { CLI_VERSION, type CliArgs, EVIDENCE_OVERRIDES, KNOWN_RULE_IDS, OVERLAP_META_BY_RULE_ID, Output, SUITE_INVALIDATING_RULE_IDS, type ScanHooks, UsageErrorDetail, buildUniversalRules, discoverRuntimeReport, err, exitForFindings, fallbackWorkspace, internalErrorMessage, isEntryPoint, isValidFindingRecord, levenshtein, main, nearestFlags, out, parseArgs, pathMatchesGlob, runBadgeCommand, runBaselineCommand, runCiInstall, runCreateRuleCommand, runDebtCommand, runDiffCommand, runDoctorCommand, runDoctorPlaywright, runExplainCommand, runFixCommand, runForensicsCommand, runHandoverCommand, runHelpCommand, runImpactCommand, runInitCommand, runPrCommentCommand, runPwReportCommand, runRulesCommand, runScan, runScanCommand, runStatsCommand, runSuppressions, runTriageCommand, usageErrorMessage };
|