mjolnir-qa 2.0.2 → 2.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/CHANGELOG.md +83 -0
- package/README.md +59 -47
- package/dist/cli.d.mts +35 -3
- package/dist/cli.mjs +3716 -205
- package/dist/mcp/stdio.mjs +5 -2
- package/dist/scan-pipeline-C_Rs4u8N.mjs +2 -0
- package/dist/{scan-pipeline-D3Yk2cef.mjs → scan-pipeline-MQGUSey3.mjs} +29 -6
- package/package.json +12 -4
- package/dist/scan-pipeline-C0ka-RmX.mjs +0 -2
package/dist/mcp/stdio.mjs
CHANGED
|
@@ -904,7 +904,7 @@ function buildEvidenceGraph(parts) {
|
|
|
904
904
|
* scripts/sync-sarif-version.cjs and guarded by the version-consistency
|
|
905
905
|
* spec. cli.ts re-exports this as CLI_VERSION.
|
|
906
906
|
*/
|
|
907
|
-
const ENGINE_VERSION = "2.0
|
|
907
|
+
const ENGINE_VERSION = "2.1.0";
|
|
908
908
|
//#endregion
|
|
909
909
|
//#region src/engine/contract-versions.ts
|
|
910
910
|
/**
|
|
@@ -14455,13 +14455,15 @@ function applyPostScanProcessing(findings, workspace, args, hooks, scanRoot, dec
|
|
|
14455
14455
|
for (const w of warnings) hooks.onConfigWarning?.(w);
|
|
14456
14456
|
applySeverityOverrides(findings, config);
|
|
14457
14457
|
const active = loadSuppressions(workspace.root).entries.filter((e) => e.status === "active");
|
|
14458
|
-
|
|
14458
|
+
hooks.onPreSuppressionFindings?.([...findings]);
|
|
14459
|
+
let suppressionCount = 0;
|
|
14459
14460
|
if (active.length > 0) {
|
|
14460
14461
|
const ruleOnly = new Set(active.filter((e) => !e.files?.length).map((e) => e.ruleId));
|
|
14461
14462
|
const kept = findings.filter((f) => {
|
|
14462
14463
|
if (ruleOnly.has(f.ruleId)) return false;
|
|
14463
14464
|
return !active.some((e) => e.files?.length && e.ruleId === f.ruleId && e.files.some((g) => pathMatchesGlob(f.file, g)));
|
|
14464
14465
|
});
|
|
14466
|
+
suppressionCount = findings.length - kept.length;
|
|
14465
14467
|
findings.length = 0;
|
|
14466
14468
|
for (const f of kept) findings.push(f);
|
|
14467
14469
|
}
|
|
@@ -14780,6 +14782,7 @@ async function runScan(args, hooks = {}) {
|
|
|
14780
14782
|
scopeUnrecognized++;
|
|
14781
14783
|
}
|
|
14782
14784
|
});
|
|
14785
|
+
hooks.onTestFilesDiscovered?.(testFiles);
|
|
14783
14786
|
const analysis = await runFileAnalysisPhase(findings, testFiles, workspace, activeRules, hooks, cache, rulesDigest, deadline, truncationReasons, declarationsByFile, fileProvenance, (ruleId, file, error) => {
|
|
14784
14787
|
rulesCrashed++;
|
|
14785
14788
|
hooks.onRuleCrash?.(ruleId, file, error);
|
|
@@ -945,7 +945,7 @@ function buildEvidenceGraph(parts) {
|
|
|
945
945
|
* scripts/sync-sarif-version.cjs and guarded by the version-consistency
|
|
946
946
|
* spec. cli.ts re-exports this as CLI_VERSION.
|
|
947
947
|
*/
|
|
948
|
-
const ENGINE_VERSION = "2.0
|
|
948
|
+
const ENGINE_VERSION = "2.1.0";
|
|
949
949
|
//#endregion
|
|
950
950
|
//#region src/engine/contract-versions.ts
|
|
951
951
|
/**
|
|
@@ -1489,6 +1489,10 @@ function globBody(glob) {
|
|
|
1489
1489
|
}
|
|
1490
1490
|
return re;
|
|
1491
1491
|
}
|
|
1492
|
+
/** Anchored full-path glob — the primitive the matcher builds on. */
|
|
1493
|
+
function globToRegExp(glob) {
|
|
1494
|
+
return new RegExp(`^${globBody(glob)}$`);
|
|
1495
|
+
}
|
|
1492
1496
|
/** Defaults-only matcher for callers that have no scan context. */
|
|
1493
1497
|
const DEFAULT_IGNORE_MATCHER = createMatcherFromPatterns([]);
|
|
1494
1498
|
/**
|
|
@@ -11946,6 +11950,22 @@ function parseManifest(filePath) {
|
|
|
11946
11950
|
if (filePath.endsWith("pyproject.toml")) return parsePyprojectToml(filePath);
|
|
11947
11951
|
if (filePath.endsWith("pom.xml")) return parsePomXml(filePath);
|
|
11948
11952
|
}
|
|
11953
|
+
/**
|
|
11954
|
+
* Given a set of starting files (e.g. test files), return all files
|
|
11955
|
+
* transitively reachable through the dependency graph.
|
|
11956
|
+
*/
|
|
11957
|
+
function getReachableFiles(fromFiles, graph) {
|
|
11958
|
+
const reachable = /* @__PURE__ */ new Set();
|
|
11959
|
+
const stack = [...fromFiles];
|
|
11960
|
+
while (stack.length > 0) {
|
|
11961
|
+
const current = stack.pop();
|
|
11962
|
+
if (current === void 0) continue;
|
|
11963
|
+
if (reachable.has(current)) continue;
|
|
11964
|
+
reachable.add(current);
|
|
11965
|
+
for (const dep of graph.getDependencies(current)) if (!reachable.has(dep)) stack.push(dep);
|
|
11966
|
+
}
|
|
11967
|
+
return [...reachable].sort();
|
|
11968
|
+
}
|
|
11949
11969
|
//#endregion
|
|
11950
11970
|
//#region src/engine/incremental-analysis.ts
|
|
11951
11971
|
/**
|
|
@@ -12521,7 +12541,7 @@ const HEADLINES = {
|
|
|
12521
12541
|
critical: "The hammer is cracked — {n} findings break its edge.",
|
|
12522
12542
|
warning: "The hammer holds — but {n} findings weigh it down.",
|
|
12523
12543
|
trusted: "Held in worthy hands — {n} findings remain.",
|
|
12524
|
-
forged: "
|
|
12544
|
+
forged: "Static score 100 — no findings on the analyzed surface.",
|
|
12525
12545
|
unmeasured: "No tests found — the hammer cannot be weighed."
|
|
12526
12546
|
};
|
|
12527
12547
|
const RUNES = {
|
|
@@ -14062,12 +14082,12 @@ function loadSuppressions(root) {
|
|
|
14062
14082
|
};
|
|
14063
14083
|
}
|
|
14064
14084
|
function renderSuppressions(report) {
|
|
14065
|
-
if (report.total === 0) return "\nNo
|
|
14085
|
+
if (report.total === 0) return "\nNo configured suppression entries.\n";
|
|
14066
14086
|
const lines = [
|
|
14067
14087
|
"",
|
|
14068
14088
|
sectionHeader("QUALITY GOVERNANCE", ui),
|
|
14069
14089
|
"",
|
|
14070
|
-
`
|
|
14090
|
+
`Configured entries: ${report.total}`,
|
|
14071
14091
|
`Active: ${report.active}`,
|
|
14072
14092
|
`Expired: ${report.expired}`,
|
|
14073
14093
|
""
|
|
@@ -14931,13 +14951,15 @@ function applyPostScanProcessing(findings, workspace, args, hooks, scanRoot, dec
|
|
|
14931
14951
|
for (const w of warnings) hooks.onConfigWarning?.(w);
|
|
14932
14952
|
applySeverityOverrides(findings, config);
|
|
14933
14953
|
const active = loadSuppressions(workspace.root).entries.filter((e) => e.status === "active");
|
|
14934
|
-
|
|
14954
|
+
hooks.onPreSuppressionFindings?.([...findings]);
|
|
14955
|
+
let suppressionCount = 0;
|
|
14935
14956
|
if (active.length > 0) {
|
|
14936
14957
|
const ruleOnly = new Set(active.filter((e) => !e.files?.length).map((e) => e.ruleId));
|
|
14937
14958
|
const kept = findings.filter((f) => {
|
|
14938
14959
|
if (ruleOnly.has(f.ruleId)) return false;
|
|
14939
14960
|
return !active.some((e) => e.files?.length && e.ruleId === f.ruleId && e.files.some((g) => pathMatchesGlob(f.file, g)));
|
|
14940
14961
|
});
|
|
14962
|
+
suppressionCount = findings.length - kept.length;
|
|
14941
14963
|
findings.length = 0;
|
|
14942
14964
|
for (const f of kept) findings.push(f);
|
|
14943
14965
|
}
|
|
@@ -15256,6 +15278,7 @@ async function runScan(args, hooks = {}) {
|
|
|
15256
15278
|
scopeUnrecognized++;
|
|
15257
15279
|
}
|
|
15258
15280
|
});
|
|
15281
|
+
hooks.onTestFilesDiscovered?.(testFiles);
|
|
15259
15282
|
const analysis = await runFileAnalysisPhase(findings, testFiles, workspace, activeRules, hooks, cache, rulesDigest, deadline, truncationReasons, declarationsByFile, fileProvenance, (ruleId, file, error) => {
|
|
15260
15283
|
rulesCrashed++;
|
|
15261
15284
|
hooks.onRuleCrash?.(ruleId, file, error);
|
|
@@ -15306,4 +15329,4 @@ async function runScan(args, hooks = {}) {
|
|
|
15306
15329
|
return result;
|
|
15307
15330
|
}
|
|
15308
15331
|
//#endregion
|
|
15309
|
-
export {
|
|
15332
|
+
export { massCeiling as $, box as A, isAdvisoryFinding as At, headlineFor as B, buildFooter as C, buildEvidenceGraph as Ct, plainContext as D, RULE_CATEGORIES as Dt, panel as E, QA_IMPACT_LABELS as Et, scoreGauge as F, buildDependencyGraph as G, EVIDENCE as H, shouldColorize as I, classifyProvenance as J, getReachableFiles as K, shouldUseAscii as L, padTo as M, palette as N, sectionHeader as O, SEVERITY_ORDER as Ot, sanitizeData as P, deductionFor as Q, wrapText as R, FLAKE_GLYPH as S, ENGINE_VERSION as St, okIcon as T, DEDUCTIONS as Tt, TINT as U, BADGE_BAND as V, TRUST as W, capForTier as X, computeAgenticProfile as Y, computeDimensions as Z, summarizeForensicVerdicts as _, ConfigValidationError as _t, applyPostScanProcessing as a, SEARCHED_FOR as at, renderSuppressions as b, isRecord as bt, discoverAndParseRuntimeReport as c, parseWorkflow as ct, isValidFindingRecord as d, parseTsFile as dt, RETIRED_RULE_IDS as et, pathMatchesGlob as f, detectFrameworks as ft, selectAdapter as g, isLintFixtureDir as gt, scan_pipeline_exports as h, globToRegExp as ht, SUITE_INVALIDATING_RULE_IDS as i, SCAN_ADAPTERS as it, measure as j, isValidCategory as jt, severityIcon as k, deriveEvidenceLevel as kt, discoverTestFilesPhase as l, parseYamlGuarded as lt, runScan as m, createIgnoreMatcher as mt, KNOWN_RULE_IDS as n, getRule as nt, assembleScanResult as o, isAzurePipelineFixture as ot, runFileAnalysisPhase as p, DEFAULT_IGNORE_MATCHER as pt, correlateFindings as q, OVERLAP_META_BY_RULE_ID as r, resolveGitPath as rt, buildUniversalRules as s, parseAzurePipeline as st, EVIDENCE_OVERRIDES as t, RULES as tt, fallbackWorkspace as u, computeCodeText as ut, loadLocalRules as v, isSuppressionActive as vt, nextStep as w, MEASURED_FP as wt, runForensics as x, parseJsonFile as xt, loadSuppressions as y, loadConfig as yt, deriveScoreState as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mjolnir-qa",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Mjölnir — the Verification Trust Engine for QA. Audits test suites and CI pipelines, reports a worthiness score and prioritized findings.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -25,15 +25,18 @@
|
|
|
25
25
|
"mcp": "node dist/mcp/stdio.mjs",
|
|
26
26
|
"test": "vitest run",
|
|
27
27
|
"bench:scan": "tsx scripts/bench-scan.ts .mjolnir --files 300",
|
|
28
|
-
"test:stress": "vitest run --config vitest.stress.config.ts",
|
|
28
|
+
"test:stress": "vitest run --config vitest.stress.config.ts && node tests/stress/soak.mjs && node tests/stress/concurrent.mjs",
|
|
29
29
|
"test:coverage": "vitest run --coverage",
|
|
30
|
+
"test:coverage:ci": "vitest run --coverage --no-file-parallelism --testTimeout=120000 --hookTimeout=120000",
|
|
30
31
|
"coverage:ratchet": "node scripts/check-coverage-ratchet.mjs",
|
|
32
|
+
"audit:ci": "npm audit --audit-level=moderate",
|
|
31
33
|
"test:watch": "vitest",
|
|
32
34
|
"lint": "eslint . --max-warnings=0 && prettier --check .",
|
|
33
35
|
"format": "prettier --write .",
|
|
34
36
|
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.test.json && tsx scripts/typecheck-fixtures.ts",
|
|
35
|
-
"certify": "npm run lint && npm run typecheck && npm run test && npx vitest run tests/contract/",
|
|
36
|
-
"ci
|
|
37
|
+
"certify": "npm run build && npm run lint && npm run typecheck && npm run test && npx vitest run tests/contract/",
|
|
38
|
+
"certify:ci": "npm run build && npm run lint && npm run typecheck && npm run test:coverage:ci && npx vitest run tests/contract/",
|
|
39
|
+
"ci-local": "npm run certify:ci && npm run test:property && npm run test:fuzz && npm run coverage:ratchet && npm run audit:ci && npm run brand:doctor && npm run brand:doctor:selftest && npm run brand:fonts:check && npm run site:doctor && npm run ci-local:parity",
|
|
37
40
|
"golden:update": "tsx tests/golden/gen.ts",
|
|
38
41
|
"detector-hashes:update": "tsx scripts/generate-detector-hashes.ts",
|
|
39
42
|
"corpus:regression": "tsx tests/corpus/audit.ts",
|
|
@@ -66,6 +69,9 @@
|
|
|
66
69
|
"changelog:check": "tsx scripts/check-changelog.ts --expect-version $(node -p \"require('./package.json').version\")",
|
|
67
70
|
"reporter:version-check": "tsx scripts/check-reporter-version.ts",
|
|
68
71
|
"self-scan": "node dist/cli.mjs .",
|
|
72
|
+
"ci-local:parity": "node scripts/check-ci-local-parity.mjs",
|
|
73
|
+
"test:property": "node scripts/check-property-runs.mjs && vitest run --config vitest.property.config.ts",
|
|
74
|
+
"test:fuzz": "vitest run --config vitest.fuzz.config.ts",
|
|
69
75
|
"prepare": "husky",
|
|
70
76
|
"prepublishOnly": "npm run build",
|
|
71
77
|
"docs:architecture": "tsx scripts/generate-readme-architecture.ts",
|
|
@@ -107,6 +113,8 @@
|
|
|
107
113
|
"yaml": "^2.5.0"
|
|
108
114
|
},
|
|
109
115
|
"devDependencies": {
|
|
116
|
+
"@commitlint/cli": "^19.8.1",
|
|
117
|
+
"@commitlint/config-conventional": "^19.8.1",
|
|
110
118
|
"@eslint/js": "^10.0.1",
|
|
111
119
|
"@playwright/test": "^1.50.0",
|
|
112
120
|
"@types/node": "^26.4.0",
|