@vyuhlabs/dxkit 2.4.8 → 2.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/CHANGELOG.md +235 -0
- package/README.md +360 -439
- package/dist/analyzers/security/aggregator.d.ts.map +1 -1
- package/dist/analyzers/security/aggregator.js +4 -46
- package/dist/analyzers/security/aggregator.js.map +1 -1
- package/dist/analyzers/tools/fingerprint.d.ts +91 -26
- package/dist/analyzers/tools/fingerprint.d.ts.map +1 -1
- package/dist/analyzers/tools/fingerprint.js +111 -22
- package/dist/analyzers/tools/fingerprint.js.map +1 -1
- package/dist/analyzers/tools/generic.d.ts.map +1 -1
- package/dist/analyzers/tools/generic.js +6 -1
- package/dist/analyzers/tools/generic.js.map +1 -1
- package/dist/analyzers/tools/gitleaks.d.ts +24 -1
- package/dist/analyzers/tools/gitleaks.d.ts.map +1 -1
- package/dist/analyzers/tools/gitleaks.js +20 -11
- package/dist/analyzers/tools/gitleaks.js.map +1 -1
- package/dist/analyzers/types.d.ts +6 -4
- package/dist/analyzers/types.d.ts.map +1 -1
- package/dist/baseline/baseline-file.d.ts +104 -0
- package/dist/baseline/baseline-file.d.ts.map +1 -0
- package/dist/baseline/baseline-file.js +110 -0
- package/dist/baseline/baseline-file.js.map +1 -0
- package/dist/baseline/check-renderers.d.ts +108 -0
- package/dist/baseline/check-renderers.d.ts.map +1 -0
- package/dist/baseline/check-renderers.js +379 -0
- package/dist/baseline/check-renderers.js.map +1 -0
- package/dist/baseline/check.d.ts +127 -0
- package/dist/baseline/check.d.ts.map +1 -0
- package/dist/baseline/check.js +462 -0
- package/dist/baseline/check.js.map +1 -0
- package/dist/baseline/content-hash.d.ts +83 -0
- package/dist/baseline/content-hash.d.ts.map +1 -0
- package/dist/baseline/content-hash.js +131 -0
- package/dist/baseline/content-hash.js.map +1 -0
- package/dist/baseline/create.d.ts +96 -0
- package/dist/baseline/create.d.ts.map +1 -0
- package/dist/baseline/create.js +339 -0
- package/dist/baseline/create.js.map +1 -0
- package/dist/baseline/entry-to-located.d.ts +35 -0
- package/dist/baseline/entry-to-located.d.ts.map +1 -0
- package/dist/baseline/entry-to-located.js +72 -0
- package/dist/baseline/entry-to-located.js.map +1 -0
- package/dist/baseline/finding-identity.d.ts +47 -0
- package/dist/baseline/finding-identity.d.ts.map +1 -0
- package/dist/baseline/finding-identity.js +292 -0
- package/dist/baseline/finding-identity.js.map +1 -0
- package/dist/baseline/git-aware-match.d.ts +146 -0
- package/dist/baseline/git-aware-match.d.ts.map +1 -0
- package/dist/baseline/git-aware-match.js +439 -0
- package/dist/baseline/git-aware-match.js.map +1 -0
- package/dist/baseline/policy.d.ts +171 -0
- package/dist/baseline/policy.d.ts.map +1 -0
- package/dist/baseline/policy.js +206 -0
- package/dist/baseline/policy.js.map +1 -0
- package/dist/baseline/producers/health.d.ts +30 -0
- package/dist/baseline/producers/health.d.ts.map +1 -0
- package/dist/baseline/producers/health.js +42 -0
- package/dist/baseline/producers/health.js.map +1 -0
- package/dist/baseline/producers/index.d.ts +164 -0
- package/dist/baseline/producers/index.d.ts.map +1 -0
- package/dist/baseline/producers/index.js +200 -0
- package/dist/baseline/producers/index.js.map +1 -0
- package/dist/baseline/producers/licenses.d.ts +23 -0
- package/dist/baseline/producers/licenses.d.ts.map +1 -0
- package/dist/baseline/producers/licenses.js +46 -0
- package/dist/baseline/producers/licenses.js.map +1 -0
- package/dist/baseline/producers/quality.d.ts +39 -0
- package/dist/baseline/producers/quality.d.ts.map +1 -0
- package/dist/baseline/producers/quality.js +84 -0
- package/dist/baseline/producers/quality.js.map +1 -0
- package/dist/baseline/producers/secret-hmac.d.ts +45 -0
- package/dist/baseline/producers/secret-hmac.d.ts.map +1 -0
- package/dist/baseline/producers/secret-hmac.js +70 -0
- package/dist/baseline/producers/secret-hmac.js.map +1 -0
- package/dist/baseline/producers/security.d.ts +59 -0
- package/dist/baseline/producers/security.d.ts.map +1 -0
- package/dist/baseline/producers/security.js +135 -0
- package/dist/baseline/producers/security.js.map +1 -0
- package/dist/baseline/producers/tests.d.ts +36 -0
- package/dist/baseline/producers/tests.d.ts.map +1 -0
- package/dist/baseline/producers/tests.js +69 -0
- package/dist/baseline/producers/tests.js.map +1 -0
- package/dist/baseline/salt.d.ts +45 -0
- package/dist/baseline/salt.d.ts.map +1 -0
- package/dist/baseline/salt.js +113 -0
- package/dist/baseline/salt.js.map +1 -0
- package/dist/baseline/show.d.ts +79 -0
- package/dist/baseline/show.d.ts.map +1 -0
- package/dist/baseline/show.js +233 -0
- package/dist/baseline/show.js.map +1 -0
- package/dist/baseline/types.d.ts +482 -0
- package/dist/baseline/types.d.ts.map +1 -0
- package/dist/baseline/types.js +53 -0
- package/dist/baseline/types.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +360 -81
- package/dist/cli.js.map +1 -1
- package/dist/codebase-scanner.d.ts.map +1 -1
- package/dist/codebase-scanner.js +0 -1
- package/dist/codebase-scanner.js.map +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +0 -4
- package/dist/constants.js.map +1 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +22 -25
- package/dist/doctor.js.map +1 -1
- package/dist/fail-on.d.ts +84 -0
- package/dist/fail-on.d.ts.map +1 -0
- package/dist/fail-on.js +128 -0
- package/dist/fail-on.js.map +1 -0
- package/dist/generator.d.ts.map +1 -1
- package/dist/generator.js +2 -141
- package/dist/generator.js.map +1 -1
- package/dist/languages/csharp.d.ts.map +1 -1
- package/dist/languages/csharp.js +0 -9
- package/dist/languages/csharp.js.map +1 -1
- package/dist/languages/go.d.ts.map +1 -1
- package/dist/languages/go.js +0 -15
- package/dist/languages/go.js.map +1 -1
- package/dist/languages/index.d.ts +1 -1
- package/dist/languages/index.d.ts.map +1 -1
- package/dist/languages/index.js.map +1 -1
- package/dist/languages/java.d.ts.map +1 -1
- package/dist/languages/java.js +0 -6
- package/dist/languages/java.js.map +1 -1
- package/dist/languages/kotlin.d.ts.map +1 -1
- package/dist/languages/kotlin.js +0 -11
- package/dist/languages/kotlin.js.map +1 -1
- package/dist/languages/python.d.ts.map +1 -1
- package/dist/languages/python.js +0 -15
- package/dist/languages/python.js.map +1 -1
- package/dist/languages/ruby.d.ts.map +1 -1
- package/dist/languages/ruby.js +0 -6
- package/dist/languages/ruby.js.map +1 -1
- package/dist/languages/rust.d.ts.map +1 -1
- package/dist/languages/rust.js +0 -4
- package/dist/languages/rust.js.map +1 -1
- package/dist/languages/types.d.ts +2 -28
- package/dist/languages/types.d.ts.map +1 -1
- package/dist/languages/typescript.d.ts.map +1 -1
- package/dist/languages/typescript.js +26 -4
- package/dist/languages/typescript.js.map +1 -1
- package/dist/lib.d.ts +2 -3
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +3 -6
- package/dist/lib.js.map +1 -1
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +0 -10
- package/dist/prompts.js.map +1 -1
- package/dist/report-schema.d.ts +42 -0
- package/dist/report-schema.d.ts.map +1 -0
- package/dist/report-schema.js +54 -0
- package/dist/report-schema.js.map +1 -0
- package/dist/ship-installers.d.ts +106 -0
- package/dist/ship-installers.d.ts.map +1 -0
- package/dist/ship-installers.js +415 -0
- package/dist/ship-installers.js.map +1 -0
- package/dist/types.d.ts +0 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/update.d.ts.map +1 -1
- package/dist/update.js +0 -4
- package/dist/update.js.map +1 -1
- package/package.json +17 -11
- package/templates/.claude/agents/onboarding.md +5 -4
- package/templates/.claude/agents-available/codebase-explorer.md +1 -1
- package/templates/.claude/agents-available/debugger.md +2 -2
- package/templates/.claude/agents-available/health-auditor.md +2 -2
- package/templates/.claude/commands/doctor.md +20 -12
- package/templates/.claude/skills/build/SKILL.md.template +22 -30
- package/templates/.claude/skills/deploy/SKILL.md.template +5 -25
- package/templates/.claude/skills/doctor/SKILL.md +24 -47
- package/templates/.claude/skills/gcloud/SKILL.md +5 -5
- package/templates/.claude/skills/learned/SKILL.md +1 -1
- package/templates/.claude/skills/pulumi/SKILL.md +2 -2
- package/templates/.claude/skills/quality/SKILL.md.template +4 -23
- package/templates/.claude/skills/review/SKILL.md.template +4 -3
- package/templates/.claude/skills/scaffold/SKILL.md.template +5 -15
- package/templates/.claude/skills/secrets/SKILL.md +20 -21
- package/templates/.claude/skills/session/SKILL.md +20 -31
- package/templates/.claude/skills/test/SKILL.md.template +1 -7
- package/templates/.devcontainer/devcontainer.json +81 -0
- package/templates/.devcontainer/install-agent-clis.sh +42 -0
- package/templates/.devcontainer/post-create.sh +67 -0
- package/templates/.githooks/pre-commit +55 -0
- package/templates/.githooks/pre-push +63 -0
- package/templates/.github/workflows/dxkit-baseline-refresh.yml +78 -0
- package/templates/.github/workflows/dxkit-guardrails.yml +98 -0
- package/templates/CLAUDE.md.template +62 -196
- package/dist/project-yaml.d.ts +0 -13
- package/dist/project-yaml.d.ts.map +0 -1
- package/dist/project-yaml.js +0 -188
- package/dist/project-yaml.js.map +0 -1
- package/templates/.ai/README.md +0 -117
- package/templates/.ai/prompts/execution-prompt.md +0 -9
- package/templates/.ai/prompts/planning-prompt.md +0 -18
- package/templates/.ai/prompts/session-end-template.md +0 -182
- package/templates/.ai/prompts/session-end.md +0 -132
- package/templates/.ai/prompts/session-start.md +0 -109
- package/templates/.ai/prompts/step-by-step.md +0 -113
- package/templates/.ai/sessions/.gitkeep +0 -0
- package/templates/.claude/commands/setup-pr-review.md +0 -72
- package/templates/.devcontainer/Dockerfile.dev.template +0 -89
- package/templates/.devcontainer/devcontainer.json.template +0 -184
- package/templates/.devcontainer/docker-compose.yml.template +0 -105
- package/templates/.devcontainer/init-scripts/01-init.sql.template +0 -12
- package/templates/.devcontainer/post-create.sh.template +0 -298
- package/templates/.github/workflows/ci.yml.template +0 -399
- package/templates/.github/workflows/quality.yml.template +0 -376
- package/templates/.pre-commit-config.yaml.template +0 -106
- package/templates/.project/config/edit_config.py +0 -275
- package/templates/.project/config/project_config.py +0 -894
- package/templates/.project/scripts/codegen/generate-all.sh +0 -20
- package/templates/.project/scripts/codegen/validate-all.sh +0 -17
- package/templates/.project/scripts/docs/generate-all.sh +0 -30
- package/templates/.project/scripts/docs/serve.sh +0 -20
- package/templates/.project/scripts/quality/fix-all.sh +0 -138
- package/templates/.project/scripts/quality/lint-go.sh +0 -34
- package/templates/.project/scripts/quality/lint-python.sh +0 -54
- package/templates/.project/scripts/quality/run-all.sh +0 -497
- package/templates/.project/scripts/session/commit.sh +0 -70
- package/templates/.project/scripts/session/create-pr.sh +0 -165
- package/templates/.project/scripts/session/end.sh +0 -207
- package/templates/.project/scripts/session/start.sh +0 -233
- package/templates/.project/scripts/setup/doctor.sh +0 -404
- package/templates/.project/scripts/setup/interactive-setup.sh +0 -585
- package/templates/.project/scripts/sync/sync-template.sh +0 -328
- package/templates/.project/scripts/test/run-all.sh +0 -179
- package/templates/.project/scripts/test/run-quick.sh +0 -25
- package/templates/Makefile +0 -514
- package/templates/config/versions.yaml +0 -57
- package/templates/configs/go/.golangci.yml.template +0 -172
- package/templates/configs/go/go.mod.template +0 -15
- package/templates/configs/java/README.md +0 -6
- package/templates/configs/kotlin/README.md +0 -6
- package/templates/configs/node/package.json.template +0 -67
- package/templates/configs/node/tsconfig.json.template +0 -53
- package/templates/configs/python/pyproject.toml.template +0 -92
- package/templates/configs/python/pytest.ini.template +0 -64
- package/templates/configs/python/ruff.toml.template +0 -79
- package/templates/configs/ruby/README.md +0 -6
- package/templates/configs/rust/Cargo.toml.template +0 -51
- package/templates/configs/shared/.editorconfig +0 -67
- package/templates/scripts/validate-templates.sh +0 -449
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Git-aware match — pairs prior-run identities with current-run
|
|
4
|
+
* identities through the lens of `git diff baseSha headSha`.
|
|
5
|
+
*
|
|
6
|
+
* The line-bucket identity scheme used by code/secret/config/hygiene
|
|
7
|
+
* findings tolerates ±2 lines of vertical drift. Anything past that
|
|
8
|
+
* appears to a naive set-diff as "removed + added" even though
|
|
9
|
+
* semantically the finding hasn't changed — it just moved with the
|
|
10
|
+
* surrounding code. This module closes the gap.
|
|
11
|
+
*
|
|
12
|
+
* Algorithm:
|
|
13
|
+
*
|
|
14
|
+
* 1. Exact identity match — every finding present in both runs
|
|
15
|
+
* under the same fingerprint is `persisted` immediately.
|
|
16
|
+
*
|
|
17
|
+
* 2. For each finding in the `removed` set that carries a file +
|
|
18
|
+
* line locator: ask git to map its base-line through the diff
|
|
19
|
+
* to the corresponding head-line. If the `added` set contains
|
|
20
|
+
* a finding at the same `(file, rule, mappedLine)`, the two
|
|
21
|
+
* represent the same underlying issue moved by the diff —
|
|
22
|
+
* move both to `persisted`.
|
|
23
|
+
*
|
|
24
|
+
* 3. Whatever remains in `added` and `removed` is genuinely new
|
|
25
|
+
* or genuinely gone.
|
|
26
|
+
*
|
|
27
|
+
* Fallback: when git history is unavailable (no `.git`, baseSha not
|
|
28
|
+
* reachable, file deleted, etc.) the module degrades to plain
|
|
29
|
+
* set-diff matching — the same behavior `matchAcrossRuns` produces
|
|
30
|
+
* on its own. Callers in shallow-clone CI or non-git workflows get
|
|
31
|
+
* a working (if less precise) result.
|
|
32
|
+
*
|
|
33
|
+
* Known limitations (Sprint 0 v1):
|
|
34
|
+
* - File renames are not auto-tracked. A renamed file looks like
|
|
35
|
+
* "removed prior + added current"; future iterations will use
|
|
36
|
+
* `git log --follow` or `git diff -M` rename detection to close
|
|
37
|
+
* this gap.
|
|
38
|
+
* - Cross-file refactors (function extracted to a new file) are
|
|
39
|
+
* reported as removed-and-added.
|
|
40
|
+
* - When the line-bucket mapping fails on context edits (tool
|
|
41
|
+
* reports finding at a slightly different line in head than the
|
|
42
|
+
* diff predicts), we fall back to "unmatched." Sprint 0.x adds
|
|
43
|
+
* a content-hash fallback for this class.
|
|
44
|
+
*/
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
exports.mapLineThroughDiff = mapLineThroughDiff;
|
|
47
|
+
exports.gitAwareMatch = gitAwareMatch;
|
|
48
|
+
const child_process_1 = require("child_process");
|
|
49
|
+
const finding_identity_1 = require("./finding-identity");
|
|
50
|
+
/** Confidence assigned to a git-mapped pair when the candidate sits
|
|
51
|
+
* on exactly the mapped line. Slightly below 1.0 so consumers can
|
|
52
|
+
* tell apart "exact identity match" (1.0) from "different identity
|
|
53
|
+
* but same finding through diff" (0.95). */
|
|
54
|
+
const CONFIDENCE_GIT_EXACT = 0.95;
|
|
55
|
+
/** Confidence when the candidate sits within ±2 lines of the mapped
|
|
56
|
+
* line — scanners often shift the reported line slightly across
|
|
57
|
+
* re-runs even when nothing semantic changed. */
|
|
58
|
+
const CONFIDENCE_GIT_FUZZ = 0.88;
|
|
59
|
+
/** Range of the line-fuzz lookup window. */
|
|
60
|
+
const LINE_FUZZ_RANGE = 2;
|
|
61
|
+
/** Confidence assigned to a content-hash pair. Below git-line-fuzz
|
|
62
|
+
* so the policy's per-severity confidence thresholds naturally
|
|
63
|
+
* distinguish "matched via git diff" from "matched via context
|
|
64
|
+
* bytes alone." For low-severity findings (default threshold 0.90),
|
|
65
|
+
* a content-hash pair demotes to `'uncertain'`; for critical
|
|
66
|
+
* findings (threshold 0.75), it passes through cleanly. */
|
|
67
|
+
const CONFIDENCE_CONTENT_HASH = 0.8;
|
|
68
|
+
/**
|
|
69
|
+
* Map a 1-based line number in `baseSha`'s version of `file` to its
|
|
70
|
+
* corresponding 1-based line in `headSha`. Returns `null` when the
|
|
71
|
+
* line was deleted, the file was removed, or git couldn't produce a
|
|
72
|
+
* diff for any reason.
|
|
73
|
+
*
|
|
74
|
+
* Implementation runs `git diff --unified=0 baseSha headSha -- file`
|
|
75
|
+
* and walks the resulting `@@ -A,B +C,D @@` hunks. Pure-ish: the
|
|
76
|
+
* only impurity is the git subprocess; the parser is deterministic
|
|
77
|
+
* over its input.
|
|
78
|
+
*/
|
|
79
|
+
function mapLineThroughDiff(opts) {
|
|
80
|
+
const oldFile = opts.oldFile ?? opts.file;
|
|
81
|
+
const newFile = opts.newFile ?? opts.file ?? oldFile;
|
|
82
|
+
if (!oldFile || !newFile) {
|
|
83
|
+
throw new Error('mapLineThroughDiff requires `file` or both `oldFile` + `newFile`');
|
|
84
|
+
}
|
|
85
|
+
let diff;
|
|
86
|
+
try {
|
|
87
|
+
diff = (0, child_process_1.execFileSync)('git', [
|
|
88
|
+
'diff',
|
|
89
|
+
'--unified=0',
|
|
90
|
+
'--no-color',
|
|
91
|
+
'--find-renames',
|
|
92
|
+
opts.baseSha,
|
|
93
|
+
opts.headSha,
|
|
94
|
+
'--',
|
|
95
|
+
oldFile,
|
|
96
|
+
...(newFile !== oldFile ? [newFile] : []),
|
|
97
|
+
], { cwd: opts.cwd, encoding: 'utf8' });
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// File missing in one revision, git not available, sha unreachable — any
|
|
101
|
+
// of these defeats the mapping. Caller treats null as "unmatched."
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
if (!diff.trim()) {
|
|
105
|
+
// Identical between revisions: line numbers are 1:1.
|
|
106
|
+
return opts.baseLine;
|
|
107
|
+
}
|
|
108
|
+
return walkHunks(diff, opts.baseLine);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Parse `@@ -oldStart,oldCount +newStart,newCount @@` hunk headers
|
|
112
|
+
* and resolve `baseLine` to its post-diff line number. Pure
|
|
113
|
+
* function over the diff text.
|
|
114
|
+
*
|
|
115
|
+
* A line falls into one of three regions:
|
|
116
|
+
* - Before any hunk that affects it: shifted only by the
|
|
117
|
+
* accumulated net delta of earlier hunks.
|
|
118
|
+
* - Inside a hunk's deletion span: removed by this diff,
|
|
119
|
+
* returns null.
|
|
120
|
+
* - After all hunks: shifted by the full accumulated net delta.
|
|
121
|
+
*/
|
|
122
|
+
function walkHunks(diff, baseLine) {
|
|
123
|
+
const hunkRe = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/gm;
|
|
124
|
+
let match;
|
|
125
|
+
let cumulativeShift = 0;
|
|
126
|
+
while ((match = hunkRe.exec(diff)) !== null) {
|
|
127
|
+
const oldStart = parseInt(match[1], 10);
|
|
128
|
+
const oldCount = match[2] !== undefined ? parseInt(match[2], 10) : 1;
|
|
129
|
+
const newCount = match[4] !== undefined ? parseInt(match[4], 10) : 1;
|
|
130
|
+
const oldEnd = oldStart + oldCount - 1;
|
|
131
|
+
if (baseLine < oldStart) {
|
|
132
|
+
// Line lies before this hunk — earlier shifts apply, this hunk doesn't.
|
|
133
|
+
return baseLine + cumulativeShift;
|
|
134
|
+
}
|
|
135
|
+
if (oldCount > 0 && baseLine >= oldStart && baseLine <= oldEnd) {
|
|
136
|
+
// Line was deleted by this hunk.
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
cumulativeShift += newCount - oldCount;
|
|
140
|
+
}
|
|
141
|
+
return baseLine + cumulativeShift;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Composite matcher. Three passes, decreasing in match strength:
|
|
145
|
+
*
|
|
146
|
+
* 1. Location-aware pairing (when git is available): for each
|
|
147
|
+
* line-anchored prior finding, map its base line to the
|
|
148
|
+
* corresponding head line via `git diff`, then look up a
|
|
149
|
+
* current finding at `(effectivePath, rule, mappedLine)`. The
|
|
150
|
+
* effective path is the prior path translated through the
|
|
151
|
+
* rename map; status is `'relocated'` when the path changed,
|
|
152
|
+
* `'persisted'` when it didn't.
|
|
153
|
+
* Lookups try the exact mapped line first (confidence 0.95),
|
|
154
|
+
* then a ±2 fuzz window (confidence 0.88).
|
|
155
|
+
*
|
|
156
|
+
* 1.5. Content-hash pairing (when both sides carry content
|
|
157
|
+
* hashes): match prior+current by `(canonicalRule,
|
|
158
|
+
* contentHash)`. Runs regardless of git reachability — the
|
|
159
|
+
* hash is file-content-derived and doesn't need git. Catches
|
|
160
|
+
* cases git can't (shallow clone, force-pushed baseline) and
|
|
161
|
+
* cases git misses (line-bucket boundary shifts where the
|
|
162
|
+
* surrounding context survived intact). Confidence 0.80 — the
|
|
163
|
+
* policy's per-severity thresholds naturally tune whether to
|
|
164
|
+
* trust this layer.
|
|
165
|
+
*
|
|
166
|
+
* 2. Multiset exact-identity diff over whatever remains. Catches:
|
|
167
|
+
* - findings without a file-line locator (dep-vuln, license,
|
|
168
|
+
* symbol-based coverage-gap, duplication)
|
|
169
|
+
* - line-anchored findings whose locations didn't survive
|
|
170
|
+
* the diff but whose fingerprints happen to coincide
|
|
171
|
+
* across runs
|
|
172
|
+
* - everything when git history is unreachable (`baseSha`
|
|
173
|
+
* missing) and pass 1 was skipped
|
|
174
|
+
*
|
|
175
|
+
* Why location-first: the line-bucket fingerprint scheme can produce
|
|
176
|
+
* spurious "persisted" matches when two findings of the same rule
|
|
177
|
+
* in the same file naturally shift into each other's buckets. Pass 1
|
|
178
|
+
* pairs them by real diff position, which is what a developer
|
|
179
|
+
* intuitively expects. Pass 1.5 catches the cases where pass 1 isn't
|
|
180
|
+
* available; pass 2 handles content-independent identity kinds.
|
|
181
|
+
*/
|
|
182
|
+
function gitAwareMatch(prior, current, opts) {
|
|
183
|
+
const headSha = opts.headSha ?? 'HEAD';
|
|
184
|
+
const reachability = checkShaReachable(opts.cwd, opts.baseSha);
|
|
185
|
+
const pairs = [];
|
|
186
|
+
const priorMatched = new Set();
|
|
187
|
+
const currentMatched = new Set();
|
|
188
|
+
if (reachability.ok) {
|
|
189
|
+
const renames = readRenameMap(opts.cwd, opts.baseSha, headSha);
|
|
190
|
+
// Index current findings by (file, rule, line). One key holds at
|
|
191
|
+
// most one entry — the multiset diff in pass 2 picks up any
|
|
192
|
+
// collisions left after location pairing.
|
|
193
|
+
const currentByLocation = new Map();
|
|
194
|
+
for (const c of current) {
|
|
195
|
+
if (!c.file || c.line === undefined || !c.rule)
|
|
196
|
+
continue;
|
|
197
|
+
const key = locationKey(c.file, c.rule, c.line);
|
|
198
|
+
const bucket = currentByLocation.get(key);
|
|
199
|
+
if (bucket)
|
|
200
|
+
bucket.push(c);
|
|
201
|
+
else
|
|
202
|
+
currentByLocation.set(key, [c]);
|
|
203
|
+
}
|
|
204
|
+
const takeAt = (key) => {
|
|
205
|
+
const bucket = currentByLocation.get(key);
|
|
206
|
+
if (!bucket || bucket.length === 0)
|
|
207
|
+
return undefined;
|
|
208
|
+
const head = bucket.shift();
|
|
209
|
+
if (bucket.length === 0)
|
|
210
|
+
currentByLocation.delete(key);
|
|
211
|
+
return head;
|
|
212
|
+
};
|
|
213
|
+
for (const p of prior) {
|
|
214
|
+
if (!p.file || p.line === undefined || !p.rule)
|
|
215
|
+
continue;
|
|
216
|
+
const effectivePath = renames.get(p.file) ?? p.file;
|
|
217
|
+
const pathChanged = effectivePath !== p.file;
|
|
218
|
+
const mappedLine = mapLineThroughDiff({
|
|
219
|
+
cwd: opts.cwd,
|
|
220
|
+
baseSha: opts.baseSha,
|
|
221
|
+
headSha,
|
|
222
|
+
oldFile: p.file,
|
|
223
|
+
newFile: effectivePath,
|
|
224
|
+
baseLine: p.line,
|
|
225
|
+
});
|
|
226
|
+
if (mappedLine === null)
|
|
227
|
+
continue;
|
|
228
|
+
// Exact mapped line first.
|
|
229
|
+
let candidate = takeAt(locationKey(effectivePath, p.rule, mappedLine));
|
|
230
|
+
let confidence = CONFIDENCE_GIT_EXACT;
|
|
231
|
+
let fuzzDelta = 0;
|
|
232
|
+
// Line-fuzz fallback: scanners drift the reported line by 1-2
|
|
233
|
+
// lines on re-runs. Walk outward from the mapped line.
|
|
234
|
+
if (!candidate) {
|
|
235
|
+
for (let delta = 1; delta <= LINE_FUZZ_RANGE; delta++) {
|
|
236
|
+
for (const offset of [-delta, delta]) {
|
|
237
|
+
const c2 = takeAt(locationKey(effectivePath, p.rule, mappedLine + offset));
|
|
238
|
+
if (c2) {
|
|
239
|
+
candidate = c2;
|
|
240
|
+
confidence = CONFIDENCE_GIT_FUZZ;
|
|
241
|
+
fuzzDelta = offset;
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (candidate)
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (!candidate)
|
|
250
|
+
continue;
|
|
251
|
+
priorMatched.add(p);
|
|
252
|
+
currentMatched.add(candidate);
|
|
253
|
+
const reasons = [
|
|
254
|
+
{
|
|
255
|
+
code: 'git-line-' + (fuzzDelta === 0 ? 'exact' : 'fuzz'),
|
|
256
|
+
detail: fuzzDelta === 0
|
|
257
|
+
? `git diff mapped ${p.file}:${p.line} to ${effectivePath}:${mappedLine}`
|
|
258
|
+
: `git diff mapped ${p.file}:${p.line} to ${effectivePath}:${mappedLine}; ` +
|
|
259
|
+
`current finding sits ${fuzzDelta > 0 ? '+' : ''}${fuzzDelta} line(s) off (within fuzz window)`,
|
|
260
|
+
},
|
|
261
|
+
];
|
|
262
|
+
if (pathChanged) {
|
|
263
|
+
reasons.unshift({
|
|
264
|
+
code: 'git-rename',
|
|
265
|
+
detail: `file renamed: ${p.file} → ${effectivePath}`,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
pairs.push({
|
|
269
|
+
priorId: p.id,
|
|
270
|
+
currentId: candidate.id,
|
|
271
|
+
status: pathChanged ? 'relocated' : 'persisted',
|
|
272
|
+
confidence,
|
|
273
|
+
reasons,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
// Pass 1.5 — content-hash fallback. Pairs prior+current findings
|
|
278
|
+
// by `(canonicalRule, contentHash)` when both sides carry a
|
|
279
|
+
// content hash (stamped by the producer). Runs regardless of git
|
|
280
|
+
// reachability — content hashes are file-content-derived and
|
|
281
|
+
// don't need git to compare. Confidence is below the git-line
|
|
282
|
+
// tier so the policy classifier's per-severity thresholds tune
|
|
283
|
+
// whether to trust the match.
|
|
284
|
+
{
|
|
285
|
+
const currentByContent = new Map();
|
|
286
|
+
for (const c of current) {
|
|
287
|
+
if (currentMatched.has(c))
|
|
288
|
+
continue;
|
|
289
|
+
if (!c.contentHash || !c.rule)
|
|
290
|
+
continue;
|
|
291
|
+
const key = contentKey(c.rule, c.contentHash);
|
|
292
|
+
const bucket = currentByContent.get(key);
|
|
293
|
+
if (bucket)
|
|
294
|
+
bucket.push(c);
|
|
295
|
+
else
|
|
296
|
+
currentByContent.set(key, [c]);
|
|
297
|
+
}
|
|
298
|
+
const takeContent = (key) => {
|
|
299
|
+
const bucket = currentByContent.get(key);
|
|
300
|
+
if (!bucket || bucket.length === 0)
|
|
301
|
+
return undefined;
|
|
302
|
+
const head = bucket.shift();
|
|
303
|
+
if (bucket.length === 0)
|
|
304
|
+
currentByContent.delete(key);
|
|
305
|
+
return head;
|
|
306
|
+
};
|
|
307
|
+
for (const p of prior) {
|
|
308
|
+
if (priorMatched.has(p))
|
|
309
|
+
continue;
|
|
310
|
+
if (!p.contentHash || !p.rule)
|
|
311
|
+
continue;
|
|
312
|
+
const candidate = takeContent(contentKey(p.rule, p.contentHash));
|
|
313
|
+
if (!candidate)
|
|
314
|
+
continue;
|
|
315
|
+
priorMatched.add(p);
|
|
316
|
+
currentMatched.add(candidate);
|
|
317
|
+
const pathChanged = !!(p.file && candidate.file && p.file !== candidate.file);
|
|
318
|
+
pairs.push({
|
|
319
|
+
priorId: p.id,
|
|
320
|
+
currentId: candidate.id,
|
|
321
|
+
status: pathChanged ? 'relocated' : 'persisted',
|
|
322
|
+
confidence: CONFIDENCE_CONTENT_HASH,
|
|
323
|
+
reasons: [
|
|
324
|
+
{
|
|
325
|
+
code: 'content-hash',
|
|
326
|
+
detail: pathChanged
|
|
327
|
+
? `content-hash match across rename: ${p.file ?? '?'} → ${candidate.file ?? '?'}`
|
|
328
|
+
: 'content-hash match (surrounding code byte-identical after whitespace normalization)',
|
|
329
|
+
},
|
|
330
|
+
],
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
// Pass 2 — multiset exact-id diff over leftovers.
|
|
335
|
+
const priorRemaining = [];
|
|
336
|
+
const currentRemaining = [];
|
|
337
|
+
for (const p of prior)
|
|
338
|
+
if (!priorMatched.has(p))
|
|
339
|
+
priorRemaining.push(p.id);
|
|
340
|
+
for (const c of current)
|
|
341
|
+
if (!currentMatched.has(c))
|
|
342
|
+
currentRemaining.push(c.id);
|
|
343
|
+
const exactRemaining = (0, finding_identity_1.matchAcrossRuns)(priorRemaining, currentRemaining);
|
|
344
|
+
for (const pair of exactRemaining.pairs)
|
|
345
|
+
pairs.push(pair);
|
|
346
|
+
// Flatten the legacy views from the pair list.
|
|
347
|
+
const persisted = [];
|
|
348
|
+
const added = [];
|
|
349
|
+
const removed = [];
|
|
350
|
+
for (const pair of pairs) {
|
|
351
|
+
switch (pair.status) {
|
|
352
|
+
case 'persisted':
|
|
353
|
+
case 'relocated':
|
|
354
|
+
if (pair.priorId)
|
|
355
|
+
persisted.push(pair.priorId);
|
|
356
|
+
if (pair.currentId && pair.currentId !== pair.priorId)
|
|
357
|
+
persisted.push(pair.currentId);
|
|
358
|
+
break;
|
|
359
|
+
case 'added':
|
|
360
|
+
if (pair.currentId)
|
|
361
|
+
added.push(pair.currentId);
|
|
362
|
+
break;
|
|
363
|
+
case 'removed':
|
|
364
|
+
if (pair.priorId)
|
|
365
|
+
removed.push(pair.priorId);
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return {
|
|
370
|
+
pairs,
|
|
371
|
+
persisted,
|
|
372
|
+
added,
|
|
373
|
+
removed,
|
|
374
|
+
gitAware: reachability.ok,
|
|
375
|
+
degradedReason: reachability.ok ? undefined : reachability.reason,
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
function locationKey(file, rule, line) {
|
|
379
|
+
return `${file}\0${rule}\0${line}`;
|
|
380
|
+
}
|
|
381
|
+
function contentKey(rule, contentHash) {
|
|
382
|
+
return `content\0${rule}\0${contentHash}`;
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Build a Map<oldPath, newPath> for files renamed between baseSha
|
|
386
|
+
* and headSha. Uses git's rename detection (`--find-renames`,
|
|
387
|
+
* default similarity threshold). Files that weren't renamed don't
|
|
388
|
+
* appear in the map; callers fall back to using the prior path as
|
|
389
|
+
* the effective path.
|
|
390
|
+
*/
|
|
391
|
+
function readRenameMap(cwd, baseSha, headSha) {
|
|
392
|
+
const renames = new Map();
|
|
393
|
+
let output;
|
|
394
|
+
try {
|
|
395
|
+
output = (0, child_process_1.execFileSync)('git', ['diff', '--name-status', '--find-renames', baseSha, headSha], {
|
|
396
|
+
cwd,
|
|
397
|
+
encoding: 'utf8',
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
catch {
|
|
401
|
+
return renames;
|
|
402
|
+
}
|
|
403
|
+
for (const line of output.split('\n')) {
|
|
404
|
+
// Rename lines look like: R100\told/path\tnew/path
|
|
405
|
+
// M / A / D / C lines have only one path column and are ignored here.
|
|
406
|
+
if (!line.startsWith('R'))
|
|
407
|
+
continue;
|
|
408
|
+
const parts = line.split('\t');
|
|
409
|
+
if (parts.length < 3)
|
|
410
|
+
continue;
|
|
411
|
+
renames.set(parts[1], parts[2]);
|
|
412
|
+
}
|
|
413
|
+
return renames;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Check whether the SHA exists in the repo and return a structured
|
|
417
|
+
* verdict. Distinguishes "not a git repo," "git not installed,"
|
|
418
|
+
* "valid repo but commit unreachable" — every non-ok case produces
|
|
419
|
+
* a human-readable reason for `MatchResult.degradedReason`.
|
|
420
|
+
*/
|
|
421
|
+
function checkShaReachable(cwd, sha) {
|
|
422
|
+
try {
|
|
423
|
+
(0, child_process_1.execFileSync)('git', ['rev-parse', '--git-dir'], { cwd, stdio: 'ignore' });
|
|
424
|
+
}
|
|
425
|
+
catch {
|
|
426
|
+
return { ok: false, reason: 'cwd is not a git repository (or git is not installed)' };
|
|
427
|
+
}
|
|
428
|
+
try {
|
|
429
|
+
(0, child_process_1.execFileSync)('git', ['cat-file', '-e', sha], { cwd, stdio: 'ignore' });
|
|
430
|
+
return { ok: true };
|
|
431
|
+
}
|
|
432
|
+
catch {
|
|
433
|
+
return {
|
|
434
|
+
ok: false,
|
|
435
|
+
reason: `baseline commit ${sha} is not reachable in this checkout (shallow clone or force-push?)`,
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
//# sourceMappingURL=git-aware-match.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-aware-match.js","sourceRoot":"","sources":["../../src/baseline/git-aware-match.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brownfield policy + status classifier.
|
|
3
|
+
*
|
|
4
|
+
* The matcher in `git-aware-match.ts` emits raw `MatchPair`s with one
|
|
5
|
+
* of four statuses (persisted / relocated / added / removed) plus a
|
|
6
|
+
* confidence score and structured reasons. The guardrail check needs
|
|
7
|
+
* a richer taxonomy — the difference between "developer introduced
|
|
8
|
+
* a new finding" and "a scanner update surfaced a finding that was
|
|
9
|
+
* always there" matters enormously for whether to block a PR.
|
|
10
|
+
*
|
|
11
|
+
* This module is the bridge. It takes a `MatchPair` plus optional
|
|
12
|
+
* context (severity, scanner-version diff, config diff) and a
|
|
13
|
+
* `BrownfieldPolicy`, then emits a `ClassifyResult` carrying the
|
|
14
|
+
* post-policy `FindingStatus`, the block/warn verdict, and the
|
|
15
|
+
* composed reason chain.
|
|
16
|
+
*
|
|
17
|
+
* Pure module — no I/O, deterministic over its inputs.
|
|
18
|
+
*
|
|
19
|
+
* Producer wiring note: today's classifier emits a subset of the full
|
|
20
|
+
* `FindingStatus` taxonomy. Reservations for `probable_existing`,
|
|
21
|
+
* `newly_detected`, and `fixed` are declared in the type space so
|
|
22
|
+
* Phase 3's baseline-metadata work can light them up incrementally
|
|
23
|
+
* without re-shaping consumer code.
|
|
24
|
+
*/
|
|
25
|
+
import type { FindingSeverity, FindingStatus, MatchPair, MatchReason } from './types';
|
|
26
|
+
/**
|
|
27
|
+
* Per-finding-kind overrides that escalate specific guardrail rules
|
|
28
|
+
* beyond the generic `block` / `warn` lists. Each rule maps to a
|
|
29
|
+
* common product-level concern; the classifier checks them when the
|
|
30
|
+
* relevant context fields are present.
|
|
31
|
+
*/
|
|
32
|
+
export interface BrownfieldBlockRules {
|
|
33
|
+
/** Block any newly-introduced secret regardless of confidence. */
|
|
34
|
+
readonly newSecret?: boolean;
|
|
35
|
+
/** Block any newly-introduced critical security finding. */
|
|
36
|
+
readonly newCriticalSecurity?: boolean;
|
|
37
|
+
/** Block any newly-introduced high-severity security finding. */
|
|
38
|
+
readonly newHighSecurity?: boolean;
|
|
39
|
+
/** Block any newly-introduced critical dependency vulnerability. */
|
|
40
|
+
readonly newCriticalDependencyVulnerability?: boolean;
|
|
41
|
+
/** Block any newly-introduced high-severity reachable dep vuln. */
|
|
42
|
+
readonly newHighReachableDependencyVulnerability?: boolean;
|
|
43
|
+
/** Block when an untested source file is added in a changed file. */
|
|
44
|
+
readonly newUntestedChangedSource?: boolean;
|
|
45
|
+
/** Block any newly-introduced severe quality issue in changed files. */
|
|
46
|
+
readonly newSevereQualityIssueInChangedFiles?: boolean;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Brownfield-mode policy. The product promise — "existing debt is
|
|
50
|
+
* allowed; new regressions are blocked" — flows from these settings.
|
|
51
|
+
*/
|
|
52
|
+
export interface BrownfieldPolicy {
|
|
53
|
+
readonly mode: 'brownfield';
|
|
54
|
+
/** Statuses that fail the guardrail check (non-zero exit code). */
|
|
55
|
+
readonly block: ReadonlyArray<FindingStatus>;
|
|
56
|
+
/** Statuses that emit a warning but don't fail. */
|
|
57
|
+
readonly warn: ReadonlyArray<FindingStatus>;
|
|
58
|
+
/**
|
|
59
|
+
* Per-severity confidence thresholds. A `relocated` or `persisted`
|
|
60
|
+
* match with confidence below the per-severity threshold demotes
|
|
61
|
+
* to `'uncertain'` — the policy can warn rather than silently
|
|
62
|
+
* accept a low-confidence pairing.
|
|
63
|
+
*/
|
|
64
|
+
readonly confidence: Readonly<Record<FindingSeverity, number>>;
|
|
65
|
+
/** Per-kind block-on-new overrides. */
|
|
66
|
+
readonly blockRules: BrownfieldBlockRules;
|
|
67
|
+
/**
|
|
68
|
+
* Finding kinds whose `added` classification only blocks when the
|
|
69
|
+
* finding overlaps lines actually changed in the current diff.
|
|
70
|
+
*
|
|
71
|
+
* Some upstream scanners (notably semgrep on large codebases) are
|
|
72
|
+
* non-deterministic across runs — parallel rule execution + per-
|
|
73
|
+
* rule timeouts mean each run discovers a slightly different
|
|
74
|
+
* subset of the full match space. When the baseline missed a real
|
|
75
|
+
* finding and a later scan catches it on UNCHANGED code, the
|
|
76
|
+
* matcher legitimately reports `added` — but the developer
|
|
77
|
+
* didn't introduce it.
|
|
78
|
+
*
|
|
79
|
+
* For kinds listed here, an `added` finding outside the diff's
|
|
80
|
+
* changed lines gets demoted to `uncertain` (a warn status).
|
|
81
|
+
* Findings inside changed lines still block — that's where the
|
|
82
|
+
* developer actually wrote code.
|
|
83
|
+
*
|
|
84
|
+
* Default: `['code', 'hygiene']` — the kinds with confirmed
|
|
85
|
+
* scanner-wobble risk. Customers can extend (`'duplication'`,
|
|
86
|
+
* `'large-file'`) or clear it (block on everything regardless of
|
|
87
|
+
* diff overlap) via `.dxkit/policy.json`.
|
|
88
|
+
*/
|
|
89
|
+
readonly addedRequiresChangedLines: ReadonlyArray<string>;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Default brownfield policy. Captures the conservative posture from
|
|
93
|
+
* the agentic-brownfield strategy: block only on high-confidence new
|
|
94
|
+
* regressions; warn on the categories that suggest a problem might
|
|
95
|
+
* be real but might also be drift; legacy debt is permitted.
|
|
96
|
+
*
|
|
97
|
+
* Confidence thresholds: secrets + critical security demand a tight
|
|
98
|
+
* confidence threshold (a low-confidence persisted secret pairing
|
|
99
|
+
* gets demoted to uncertain and warned, not blocked). Lower-severity
|
|
100
|
+
* findings can pair on weaker signal because the cost of a false
|
|
101
|
+
* "secret is new" event is much higher than a false "TODO is new."
|
|
102
|
+
*/
|
|
103
|
+
export declare const DEFAULT_BROWNFIELD_POLICY: BrownfieldPolicy;
|
|
104
|
+
/**
|
|
105
|
+
* Contextual signals the classifier reads when available. Every field
|
|
106
|
+
* is optional — pass `{}` for the basic matcher-only classification.
|
|
107
|
+
* Phase 3 baseline-metadata work will populate these from the stored
|
|
108
|
+
* baseline + current scan envelope.
|
|
109
|
+
*/
|
|
110
|
+
export interface ClassifyContext {
|
|
111
|
+
/** Severity of the underlying finding. */
|
|
112
|
+
readonly severity?: FindingSeverity;
|
|
113
|
+
/** Kind discriminator from `IdentityInput`, for block-rule checks. */
|
|
114
|
+
readonly kind?: string;
|
|
115
|
+
/** True when the baseline's scanner / advisory-db version differs
|
|
116
|
+
* from the current scan. Reclassifies an `added` finding as
|
|
117
|
+
* `tooling_drift` rather than blocking it as a new regression. */
|
|
118
|
+
readonly scannerVersionDiffers?: boolean;
|
|
119
|
+
/** True when the baseline's `.dxkit-ignore` / policy hash differs
|
|
120
|
+
* from the current scan. Reclassifies `added` as `config_drift`. */
|
|
121
|
+
readonly configDiffers?: boolean;
|
|
122
|
+
/** True when the underlying source file's lines overlap lines
|
|
123
|
+
* changed by the current diff. Used by `newSevereQualityIssueIn
|
|
124
|
+
* ChangedFiles` and similar rules; absent context is treated as
|
|
125
|
+
* "we don't know, assume outside changed lines." */
|
|
126
|
+
readonly overlapsChangedLines?: boolean;
|
|
127
|
+
/** True when an `added` dep-vuln is on a reachable code path. */
|
|
128
|
+
readonly reachable?: boolean;
|
|
129
|
+
}
|
|
130
|
+
/** Verdict + reasoning for one classified pair. */
|
|
131
|
+
export interface ClassifyResult {
|
|
132
|
+
readonly status: FindingStatus;
|
|
133
|
+
/** Whether the policy blocks based on the classified status or a
|
|
134
|
+
* specific block-rule override. */
|
|
135
|
+
readonly blocks: boolean;
|
|
136
|
+
/** Whether the policy warns. */
|
|
137
|
+
readonly warns: boolean;
|
|
138
|
+
/** Reasons backing the status — composed of the matcher's reasons
|
|
139
|
+
* plus any classification-time additions. */
|
|
140
|
+
readonly reasons: ReadonlyArray<MatchReason>;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Classify one match pair against a brownfield policy.
|
|
144
|
+
*
|
|
145
|
+
* Pipeline:
|
|
146
|
+
* 1. Start with the matcher's `pair.status` as the candidate
|
|
147
|
+
* `FindingStatus`.
|
|
148
|
+
* 2. For `added`: check drift context. Scanner-version drift wins
|
|
149
|
+
* (more specific signal) over config drift; both demote to
|
|
150
|
+
* drift-bucket statuses regardless of severity.
|
|
151
|
+
* 3. For `persisted` / `relocated`: check confidence against the
|
|
152
|
+
* per-severity threshold. Below threshold demotes to
|
|
153
|
+
* `'uncertain'`.
|
|
154
|
+
* 4. Apply block-rule overrides: if a block-rule fires for this
|
|
155
|
+
* kind+severity combination AND the candidate status is
|
|
156
|
+
* `'added'`, the result blocks even if `'added'` weren't in the
|
|
157
|
+
* policy's block list.
|
|
158
|
+
* 5. Apply the policy's `block` / `warn` membership to the final
|
|
159
|
+
* status to produce the booleans.
|
|
160
|
+
*/
|
|
161
|
+
export declare function classify(pair: MatchPair, policy?: BrownfieldPolicy, context?: ClassifyContext): ClassifyResult;
|
|
162
|
+
/**
|
|
163
|
+
* Convenience: classify every pair in a match result against the
|
|
164
|
+
* same policy. Returns an array aligned with the input pair order
|
|
165
|
+
* so callers can render side-by-side. Per-pair context (severity,
|
|
166
|
+
* kind, drift flags) is supplied via the optional `contextFor`
|
|
167
|
+
* callback — callers map their `FindingId` back to the producer
|
|
168
|
+
* envelope to fill in the fields the classifier reads.
|
|
169
|
+
*/
|
|
170
|
+
export declare function classifyAll(pairs: ReadonlyArray<MatchPair>, policy?: BrownfieldPolicy, contextFor?: (pair: MatchPair) => ClassifyContext): ReadonlyArray<ClassifyResult>;
|
|
171
|
+
//# sourceMappingURL=policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/baseline/policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEtF;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACnC,kEAAkE;IAClE,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAC7B,4DAA4D;IAC5D,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IACvC,iEAAiE;IACjE,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IACnC,oEAAoE;IACpE,QAAQ,CAAC,kCAAkC,CAAC,EAAE,OAAO,CAAC;IACtD,mEAAmE;IACnE,QAAQ,CAAC,uCAAuC,CAAC,EAAE,OAAO,CAAC;IAC3D,qEAAqE;IACrE,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IAC5C,wEAAwE;IACxE,QAAQ,CAAC,mCAAmC,CAAC,EAAE,OAAO,CAAC;CACxD;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,mEAAmE;IACnE,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IAC7C,mDAAmD;IACnD,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IAC5C;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC,CAAC;IAC/D,uCAAuC;IACvC,QAAQ,CAAC,UAAU,EAAE,oBAAoB,CAAC;IAC1C;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,QAAQ,CAAC,yBAAyB,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC3D;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,yBAAyB,EAAE,gBA0BtC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,0CAA0C;IAC1C,QAAQ,CAAC,QAAQ,CAAC,EAAE,eAAe,CAAC;IACpC,sEAAsE;IACtE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;uEAEmE;IACnE,QAAQ,CAAC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IACzC;yEACqE;IACrE,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IACjC;;;yDAGqD;IACrD,QAAQ,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IACxC,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,mDAAmD;AACnD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B;wCACoC;IACpC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,gCAAgC;IAChC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB;kDAC8C;IAC9C,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;CAC9C;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,QAAQ,CACtB,IAAI,EAAE,SAAS,EACf,MAAM,GAAE,gBAA4C,EACpD,OAAO,GAAE,eAAoB,GAC5B,cAAc,CAmEhB;AA0DD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,aAAa,CAAC,SAAS,CAAC,EAC/B,MAAM,GAAE,gBAA4C,EACpD,UAAU,GAAE,CAAC,IAAI,EAAE,SAAS,KAAK,eAA4B,GAC5D,aAAa,CAAC,cAAc,CAAC,CAE/B"}
|