@zigrivers/mmr 1.3.0 → 1.4.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 +422 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/ack.d.ts +11 -0
- package/dist/commands/ack.d.ts.map +1 -0
- package/dist/commands/ack.js +123 -0
- package/dist/commands/ack.js.map +1 -0
- package/dist/commands/config.d.ts +5 -0
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +248 -14
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/jobs.d.ts.map +1 -1
- package/dist/commands/jobs.js +3 -4
- package/dist/commands/jobs.js.map +1 -1
- package/dist/commands/reconcile.d.ts.map +1 -1
- package/dist/commands/reconcile.js +12 -5
- package/dist/commands/reconcile.js.map +1 -1
- package/dist/commands/results.d.ts.map +1 -1
- package/dist/commands/results.js +13 -5
- package/dist/commands/results.js.map +1 -1
- package/dist/commands/review.d.ts +25 -0
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +457 -44
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/sessions.d.ts +58 -0
- package/dist/commands/sessions.d.ts.map +1 -0
- package/dist/commands/sessions.js +266 -0
- package/dist/commands/sessions.js.map +1 -0
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +2 -3
- package/dist/commands/status.js.map +1 -1
- package/dist/config/defaults.d.ts +2 -2
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +66 -0
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/loader.d.ts +22 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +279 -36
- package/dist/config/loader.js.map +1 -1
- package/dist/config/schema.d.ts +869 -53
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +151 -4
- package/dist/config/schema.js.map +1 -1
- package/dist/core/ack-store.d.ts +109 -0
- package/dist/core/ack-store.d.ts.map +1 -0
- package/dist/core/ack-store.js +363 -0
- package/dist/core/ack-store.js.map +1 -0
- package/dist/core/auth.d.ts +10 -1
- package/dist/core/auth.d.ts.map +1 -1
- package/dist/core/auth.js +65 -2
- package/dist/core/auth.js.map +1 -1
- package/dist/core/compensator.d.ts +32 -4
- package/dist/core/compensator.d.ts.map +1 -1
- package/dist/core/compensator.js +118 -15
- package/dist/core/compensator.js.map +1 -1
- package/dist/core/diff-introspect.d.ts +21 -0
- package/dist/core/diff-introspect.d.ts.map +1 -0
- package/dist/core/diff-introspect.js +42 -0
- package/dist/core/diff-introspect.js.map +1 -0
- package/dist/core/dispatcher.d.ts +7 -0
- package/dist/core/dispatcher.d.ts.map +1 -1
- package/dist/core/dispatcher.js +21 -3
- package/dist/core/dispatcher.js.map +1 -1
- package/dist/core/git-show.d.ts +31 -0
- package/dist/core/git-show.d.ts.map +1 -0
- package/dist/core/git-show.js +72 -0
- package/dist/core/git-show.js.map +1 -0
- package/dist/core/http-dispatcher.d.ts +20 -0
- package/dist/core/http-dispatcher.d.ts.map +1 -0
- package/dist/core/http-dispatcher.js +125 -0
- package/dist/core/http-dispatcher.js.map +1 -0
- package/dist/core/job-store.d.ts +7 -1
- package/dist/core/job-store.d.ts.map +1 -1
- package/dist/core/job-store.js +21 -1
- package/dist/core/job-store.js.map +1 -1
- package/dist/core/jsonpath.d.ts +15 -0
- package/dist/core/jsonpath.d.ts.map +1 -0
- package/dist/core/jsonpath.js +63 -0
- package/dist/core/jsonpath.js.map +1 -0
- package/dist/core/oss-examples.d.ts +18 -0
- package/dist/core/oss-examples.d.ts.map +1 -0
- package/dist/core/oss-examples.js +66 -0
- package/dist/core/oss-examples.js.map +1 -0
- package/dist/core/parser.d.ts +8 -3
- package/dist/core/parser.d.ts.map +1 -1
- package/dist/core/parser.js +157 -6
- package/dist/core/parser.js.map +1 -1
- package/dist/core/project-root.d.ts +10 -0
- package/dist/core/project-root.d.ts.map +1 -0
- package/dist/core/project-root.js +23 -0
- package/dist/core/project-root.js.map +1 -0
- package/dist/core/reconciler.d.ts +1 -1
- package/dist/core/reconciler.d.ts.map +1 -1
- package/dist/core/reconciler.js +100 -18
- package/dist/core/reconciler.js.map +1 -1
- package/dist/core/redact.d.ts +17 -0
- package/dist/core/redact.d.ts.map +1 -0
- package/dist/core/redact.js +140 -0
- package/dist/core/redact.js.map +1 -0
- package/dist/core/results-pipeline.d.ts +8 -2
- package/dist/core/results-pipeline.d.ts.map +1 -1
- package/dist/core/results-pipeline.js +50 -3
- package/dist/core/results-pipeline.js.map +1 -1
- package/dist/core/runtime-probe.d.ts +14 -0
- package/dist/core/runtime-probe.d.ts.map +1 -0
- package/dist/core/runtime-probe.js +57 -0
- package/dist/core/runtime-probe.js.map +1 -0
- package/dist/core/stable-id.d.ts +19 -0
- package/dist/core/stable-id.d.ts.map +1 -0
- package/dist/core/stable-id.js +148 -0
- package/dist/core/stable-id.js.map +1 -0
- package/dist/core/trust-mode.d.ts +29 -0
- package/dist/core/trust-mode.d.ts.map +1 -0
- package/dist/core/trust-mode.js +103 -0
- package/dist/core/trust-mode.js.map +1 -0
- package/dist/formatters/markdown.d.ts.map +1 -1
- package/dist/formatters/markdown.js +9 -0
- package/dist/formatters/markdown.js.map +1 -1
- package/dist/formatters/text.d.ts.map +1 -1
- package/dist/formatters/text.js +9 -0
- package/dist/formatters/text.js.map +1 -1
- package/dist/types.d.ts +44 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
package/dist/core/reconciler.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { SEVERITY_ORDER } from '../types.js';
|
|
2
|
-
|
|
3
|
-
return location.toLowerCase().trim();
|
|
4
|
-
}
|
|
2
|
+
import { computeFindingKey, descriptionShingle, jaccardSimilarity, normalizeLocationForKey, normalizeSuggestionForKey, shingleSize, } from './stable-id.js';
|
|
5
3
|
function higherSeverity(a, b) {
|
|
6
4
|
return SEVERITY_ORDER[a] <= SEVERITY_ORDER[b] ? a : b;
|
|
7
5
|
}
|
|
@@ -10,33 +8,70 @@ function higherSeverity(a, b) {
|
|
|
10
8
|
* consensus scoring.
|
|
11
9
|
*
|
|
12
10
|
* 1. Flatten all findings with source attribution
|
|
13
|
-
* 2. Group by
|
|
11
|
+
* 2. Group by stable finding identity
|
|
14
12
|
* 3. For each group, determine agreement, confidence, and effective severity
|
|
15
13
|
* 4. Sort by severity (P0 first)
|
|
16
14
|
*/
|
|
17
15
|
export function reconcile(channelFindings) {
|
|
18
|
-
// Step 1: Flatten with source attribution
|
|
16
|
+
// Step 1: Flatten with source attribution and stable identity data
|
|
19
17
|
const attributed = [];
|
|
20
18
|
for (const [source, findings] of Object.entries(channelFindings)) {
|
|
21
19
|
for (const finding of findings) {
|
|
22
|
-
attributed.push({
|
|
20
|
+
attributed.push({
|
|
21
|
+
...finding,
|
|
22
|
+
source,
|
|
23
|
+
finding_key: computeFindingKey(finding),
|
|
24
|
+
normalized_location: normalizeLocationForKey(finding.location),
|
|
25
|
+
normalized_category: (finding.category ?? '').toLowerCase(),
|
|
26
|
+
normalized_suggestion: normalizeSuggestionForKey(finding.suggestion),
|
|
27
|
+
shingle: new Set(descriptionShingle(finding.description)),
|
|
28
|
+
});
|
|
23
29
|
}
|
|
24
30
|
}
|
|
25
31
|
if (attributed.length === 0)
|
|
26
32
|
return [];
|
|
27
|
-
|
|
28
|
-
|
|
33
|
+
attributed.sort(compareAttributedFindings);
|
|
34
|
+
// Step 2: Group by exact stable identity, then location-anchored fuzzy description match.
|
|
35
|
+
const groups = [];
|
|
36
|
+
const keyIndex = new Map();
|
|
29
37
|
for (const finding of attributed) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
group.
|
|
33
|
-
|
|
38
|
+
// Multiple groups can share a finding_key because location normalization
|
|
39
|
+
// strips line spans. Keep same-source findings at different raw locations
|
|
40
|
+
// separate, then join later channels to the compatible raw-location group.
|
|
41
|
+
const exact = bestJoinableGroup((keyIndex.get(finding.finding_key) ?? [])
|
|
42
|
+
.filter((group) => canJoinGroup(group, finding)), finding);
|
|
43
|
+
if (exact !== undefined) {
|
|
44
|
+
exact.findings.push(finding);
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const fuzzy = bestJoinableGroup(groups.filter((group) => canJoinGroup(group, finding) &&
|
|
48
|
+
group.normalized_location === finding.normalized_location &&
|
|
49
|
+
canFuzzyJoinGroup(group, finding) &&
|
|
50
|
+
shingleSize(group.shingle) > 0 &&
|
|
51
|
+
shingleSize(finding.shingle) > 0 &&
|
|
52
|
+
jaccardSimilarity(group.shingle, finding.shingle) >= 0.7), finding);
|
|
53
|
+
if (fuzzy !== undefined) {
|
|
54
|
+
fuzzy.findings.push(finding);
|
|
55
|
+
addToKeyIndex(keyIndex, finding.finding_key, fuzzy);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const group = {
|
|
59
|
+
finding_key: finding.finding_key,
|
|
60
|
+
normalized_location: finding.normalized_location,
|
|
61
|
+
normalized_category: finding.normalized_category,
|
|
62
|
+
normalized_suggestion: finding.normalized_suggestion,
|
|
63
|
+
shingle: finding.shingle,
|
|
64
|
+
findings: [finding],
|
|
65
|
+
};
|
|
66
|
+
groups.push(group);
|
|
67
|
+
addToKeyIndex(keyIndex, finding.finding_key, group);
|
|
34
68
|
}
|
|
35
69
|
// Step 3: Reconcile each group
|
|
36
70
|
const results = [];
|
|
37
|
-
for (const group of groups
|
|
38
|
-
const
|
|
39
|
-
const
|
|
71
|
+
for (const group of groups) {
|
|
72
|
+
const findings = group.findings;
|
|
73
|
+
const sources = [...new Set(findings.map((f) => f.source))];
|
|
74
|
+
const severities = [...new Set(findings.map((f) => f.severity))];
|
|
40
75
|
const effectiveSeverity = severities.reduce(higherSeverity);
|
|
41
76
|
let agreement;
|
|
42
77
|
let confidence;
|
|
@@ -61,7 +96,7 @@ export function reconcile(channelFindings) {
|
|
|
61
96
|
: 'medium';
|
|
62
97
|
}
|
|
63
98
|
// Use the finding with the longest description as representative (deterministic)
|
|
64
|
-
const representative =
|
|
99
|
+
const representative = findings.reduce((best, current) => current.description.length > best.description.length ? current : best);
|
|
65
100
|
results.push({
|
|
66
101
|
severity: effectiveSeverity,
|
|
67
102
|
location: representative.location,
|
|
@@ -72,10 +107,14 @@ export function reconcile(channelFindings) {
|
|
|
72
107
|
confidence,
|
|
73
108
|
sources,
|
|
74
109
|
agreement,
|
|
110
|
+
finding_key: representative.finding_key,
|
|
111
|
+
description_shingle: [...representative.shingle],
|
|
75
112
|
});
|
|
76
113
|
}
|
|
77
114
|
// Step 4: Sort by severity (P0 first)
|
|
78
|
-
results.sort((a, b) => SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity]
|
|
115
|
+
results.sort((a, b) => SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity] ||
|
|
116
|
+
(a.finding_key ?? '').localeCompare(b.finding_key ?? '') ||
|
|
117
|
+
a.location.localeCompare(b.location));
|
|
79
118
|
// Auto-generate finding IDs (only backfill when absent)
|
|
80
119
|
results.forEach((f, i) => {
|
|
81
120
|
if (!f.id) {
|
|
@@ -84,13 +123,56 @@ export function reconcile(channelFindings) {
|
|
|
84
123
|
});
|
|
85
124
|
return results;
|
|
86
125
|
}
|
|
126
|
+
function compareAttributedFindings(a, b) {
|
|
127
|
+
return a.finding_key.localeCompare(b.finding_key) ||
|
|
128
|
+
a.source.localeCompare(b.source) ||
|
|
129
|
+
a.location.localeCompare(b.location) ||
|
|
130
|
+
a.description.localeCompare(b.description) ||
|
|
131
|
+
a.suggestion.localeCompare(b.suggestion);
|
|
132
|
+
}
|
|
133
|
+
function canJoinGroup(group, finding) {
|
|
134
|
+
return group.findings.every((existing) => existing.source !== finding.source || existing.location === finding.location);
|
|
135
|
+
}
|
|
136
|
+
function canFuzzyJoinGroup(group, finding) {
|
|
137
|
+
if (group.findings.some((existing) => existing.location === finding.location)) {
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
return group.normalized_category === finding.normalized_category &&
|
|
141
|
+
group.normalized_suggestion === finding.normalized_suggestion;
|
|
142
|
+
}
|
|
143
|
+
function bestJoinableGroup(groups, finding) {
|
|
144
|
+
const eligible = [...groups].sort((a, b) => compareGroupsForFinding(a, b, finding));
|
|
145
|
+
return eligible.find((group) => group.findings.some((existing) => existing.location === finding.location)) ?? eligible[0];
|
|
146
|
+
}
|
|
147
|
+
function compareGroupsForFinding(a, b, finding) {
|
|
148
|
+
return groupMatchScore(b, finding) - groupMatchScore(a, finding) ||
|
|
149
|
+
a.finding_key.localeCompare(b.finding_key) ||
|
|
150
|
+
a.normalized_location.localeCompare(b.normalized_location);
|
|
151
|
+
}
|
|
152
|
+
function groupMatchScore(group, finding) {
|
|
153
|
+
let score = 0;
|
|
154
|
+
if (group.normalized_category === finding.normalized_category)
|
|
155
|
+
score += 1;
|
|
156
|
+
if (group.normalized_suggestion === finding.normalized_suggestion)
|
|
157
|
+
score += 1;
|
|
158
|
+
return score;
|
|
159
|
+
}
|
|
160
|
+
function addToKeyIndex(keyIndex, findingKey, group) {
|
|
161
|
+
const groups = keyIndex.get(findingKey);
|
|
162
|
+
if (groups === undefined) {
|
|
163
|
+
keyIndex.set(findingKey, [group]);
|
|
164
|
+
}
|
|
165
|
+
else if (!groups.includes(group)) {
|
|
166
|
+
groups.push(group);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
87
169
|
/**
|
|
88
170
|
* Evaluate the quality gate: passes if no finding has severity at or above
|
|
89
171
|
* the threshold (i.e., no finding with SEVERITY_ORDER <= threshold order).
|
|
90
172
|
*/
|
|
91
173
|
export function evaluateGate(findings, threshold) {
|
|
92
174
|
const thresholdOrder = SEVERITY_ORDER[threshold];
|
|
93
|
-
return findings.every((f) => SEVERITY_ORDER[f.severity] > thresholdOrder);
|
|
175
|
+
return findings.every((f) => f.acknowledged === true || SEVERITY_ORDER[f.severity] > thresholdOrder);
|
|
94
176
|
}
|
|
95
177
|
/**
|
|
96
178
|
* Derive the review verdict from gate evaluation and channel health.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reconciler.js","sourceRoot":"","sources":["../../src/core/reconciler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"reconciler.js","sourceRoot":"","sources":["../../src/core/reconciler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare function isSecretKey(name: string, options?: {
|
|
2
|
+
exemptEnvNameKeys?: boolean;
|
|
3
|
+
}): boolean;
|
|
4
|
+
/**
|
|
5
|
+
* Return a new record with secret-keyed values replaced by `<redacted>`.
|
|
6
|
+
* Non-secret keys pass through unchanged.
|
|
7
|
+
*/
|
|
8
|
+
export declare function redactRecord(input: Record<string, unknown> | undefined, options?: {
|
|
9
|
+
exemptEnvNameKeys?: boolean;
|
|
10
|
+
}): Record<string, unknown>;
|
|
11
|
+
/**
|
|
12
|
+
* Return a shallow copy of `channel` with `env` and `headers` redacted via
|
|
13
|
+
* `redactRecord`. `api_key_env` (the env-var NAME, not its value) is preserved
|
|
14
|
+
* as-is because the name is non-secret and useful for debugging.
|
|
15
|
+
*/
|
|
16
|
+
export declare function redactChannel(channel: Record<string, unknown>): Record<string, unknown>;
|
|
17
|
+
//# sourceMappingURL=redact.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../src/core/redact.ts"],"names":[],"mappings":"AA+BA,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAShG;AAQD;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC1C,OAAO,GAAE;IAAE,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAAO,GAC5C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAOzB;AAyED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEvF"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Key parts whose VALUE should be redacted in any introspection output.
|
|
3
|
+
* Deliberately broad, but separator-aware: `api_key` is secret-like while
|
|
4
|
+
* harmless words such as `tokenizer`, `monkey`, or `author` are not.
|
|
5
|
+
* The top-level channel field `api_key_env` is intentionally non-secret (it
|
|
6
|
+
* stores the env-var name, not its value), but the same key inside env/headers
|
|
7
|
+
* records still carries a value and is redacted there.
|
|
8
|
+
*/
|
|
9
|
+
const SECRET_KEY_PARTS = new Set([
|
|
10
|
+
'auth',
|
|
11
|
+
'authorization',
|
|
12
|
+
'cookie',
|
|
13
|
+
'cred',
|
|
14
|
+
'credentials',
|
|
15
|
+
'creds',
|
|
16
|
+
'credential',
|
|
17
|
+
'apikey',
|
|
18
|
+
'pass',
|
|
19
|
+
'passphrase',
|
|
20
|
+
'passwd',
|
|
21
|
+
'password',
|
|
22
|
+
'secret',
|
|
23
|
+
'session',
|
|
24
|
+
'sid',
|
|
25
|
+
'signature',
|
|
26
|
+
'token',
|
|
27
|
+
]);
|
|
28
|
+
const NON_SECRET_ENV_NAME_KEYS = new Set(['api_key_env']);
|
|
29
|
+
const KEY_PART_RE = /[_.-]+|(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/;
|
|
30
|
+
const SENSITIVE_KEY_CONTEXT_PARTS = new Set(['api', 'openai', 'private', 'access']);
|
|
31
|
+
export function isSecretKey(name, options = {}) {
|
|
32
|
+
const cleanName = name.replace(/^['"]|['"]$/g, '');
|
|
33
|
+
const normalized = cleanName.toLowerCase();
|
|
34
|
+
if (options.exemptEnvNameKeys !== false && NON_SECRET_ENV_NAME_KEYS.has(normalized))
|
|
35
|
+
return false;
|
|
36
|
+
const parts = cleanName.split(KEY_PART_RE).map((part) => part.toLowerCase());
|
|
37
|
+
if (parts.some((part) => SECRET_KEY_PARTS.has(part)))
|
|
38
|
+
return true;
|
|
39
|
+
if (!parts.includes('key'))
|
|
40
|
+
return false;
|
|
41
|
+
if (parts.length === 1)
|
|
42
|
+
return true;
|
|
43
|
+
return parts.some((part) => SENSITIVE_KEY_CONTEXT_PARTS.has(part));
|
|
44
|
+
}
|
|
45
|
+
function redactValue(value, options) {
|
|
46
|
+
if (Array.isArray(value))
|
|
47
|
+
return redactList(value, options);
|
|
48
|
+
if (value && typeof value === 'object')
|
|
49
|
+
return redactRecord(value, options);
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Return a new record with secret-keyed values replaced by `<redacted>`.
|
|
54
|
+
* Non-secret keys pass through unchanged.
|
|
55
|
+
*/
|
|
56
|
+
export function redactRecord(input, options = {}) {
|
|
57
|
+
if (!input)
|
|
58
|
+
return {};
|
|
59
|
+
const out = {};
|
|
60
|
+
for (const [k, v] of Object.entries(input)) {
|
|
61
|
+
out[k] = isSecretKey(k, options) ? '<redacted>' : redactValue(v, { ...options, exemptEnvNameKeys: false });
|
|
62
|
+
}
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
function redactKeyValueString(input) {
|
|
66
|
+
const match = /^(\s*)([^:=\s]+)(\s*[:=]\s*)(.*)$/.exec(input);
|
|
67
|
+
if (!match)
|
|
68
|
+
return isSecretKey(input.trim(), { exemptEnvNameKeys: false }) ? '<redacted>' : input;
|
|
69
|
+
const [, leading, key, separator, value] = match;
|
|
70
|
+
if (isSecretKey(key, { exemptEnvNameKeys: false }) || containsNestedSecretKeyValue(value)) {
|
|
71
|
+
return `${leading}${key}${separator}<redacted>`;
|
|
72
|
+
}
|
|
73
|
+
return `${leading}${key}${separator}${value}`;
|
|
74
|
+
}
|
|
75
|
+
function containsNestedSecretKeyValue(input) {
|
|
76
|
+
const nestedKeyValueRe = /(?:^|[=:])"?([A-Za-z0-9_.-]+)"?\s*[:=]/g;
|
|
77
|
+
for (const match of input.matchAll(nestedKeyValueRe)) {
|
|
78
|
+
if (isSecretKey(match[1], { exemptEnvNameKeys: false }))
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
function redactList(input, options = { exemptEnvNameKeys: false }) {
|
|
84
|
+
const out = [];
|
|
85
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
86
|
+
const value = input[i];
|
|
87
|
+
if (typeof value === 'string' &&
|
|
88
|
+
typeof input[i + 1] === 'string' &&
|
|
89
|
+
isSecretKey(value, { exemptEnvNameKeys: false })) {
|
|
90
|
+
out.push(value, '<redacted>');
|
|
91
|
+
i += 1;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (typeof value === 'string') {
|
|
95
|
+
out.push(redactKeyValueString(value));
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (Array.isArray(value)) {
|
|
99
|
+
const key = value[0];
|
|
100
|
+
const rest = value.slice(2);
|
|
101
|
+
if (typeof key === 'string' && value.length === 1) {
|
|
102
|
+
out.push([redactKeyValueString(key)]);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (typeof key === 'string' && isSecretKey(key, { exemptEnvNameKeys: false }) && value.length >= 2) {
|
|
106
|
+
out.push([key, '<redacted>', ...redactList(rest, options)]);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
out.push(redactList(value, options));
|
|
110
|
+
}
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
114
|
+
const original = value;
|
|
115
|
+
const secretLabel = (typeof original.name === 'string' && isSecretKey(original.name, { exemptEnvNameKeys: false })) ||
|
|
116
|
+
(typeof original.key === 'string' && isSecretKey(original.key, { exemptEnvNameKeys: false }));
|
|
117
|
+
const redacted = redactRecord(value, options);
|
|
118
|
+
if (typeof original.name === 'string')
|
|
119
|
+
redacted.name = original.name;
|
|
120
|
+
if (typeof original.key === 'string')
|
|
121
|
+
redacted.key = original.key;
|
|
122
|
+
if (secretLabel && 'value' in redacted) {
|
|
123
|
+
redacted.value = '<redacted>';
|
|
124
|
+
}
|
|
125
|
+
out.push(redacted);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
out.push(value);
|
|
129
|
+
}
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Return a shallow copy of `channel` with `env` and `headers` redacted via
|
|
134
|
+
* `redactRecord`. `api_key_env` (the env-var NAME, not its value) is preserved
|
|
135
|
+
* as-is because the name is non-secret and useful for debugging.
|
|
136
|
+
*/
|
|
137
|
+
export function redactChannel(channel) {
|
|
138
|
+
return redactRecord(channel);
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=redact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.js","sourceRoot":"","sources":["../../src/core/redact.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,19 @@
|
|
|
1
|
-
import type { JobMetadata, OutputFormat, ReconciledResults } from '../types.js';
|
|
1
|
+
import type { JobMetadata, Severity, OutputFormat, ReconciledResults, ReconciledFinding } from '../types.js';
|
|
2
2
|
import type { JobStore } from './job-store.js';
|
|
3
|
+
import type { AckStore } from './ack-store.js';
|
|
3
4
|
export interface PipelineResult {
|
|
4
5
|
results: ReconciledResults;
|
|
5
6
|
formatted: string;
|
|
6
7
|
exitCode: number;
|
|
7
8
|
}
|
|
9
|
+
export interface PipelineOptions {
|
|
10
|
+
ackStore?: AckStore;
|
|
11
|
+
}
|
|
12
|
+
export declare function isBlockingFinding(finding: ReconciledFinding, threshold: Severity): boolean;
|
|
13
|
+
export declare function isAdvisoryFinding(finding: ReconciledFinding, threshold: Severity): boolean;
|
|
8
14
|
/**
|
|
9
15
|
* Run the full results pipeline: parse channel outputs, reconcile findings,
|
|
10
16
|
* derive verdict, format output.
|
|
11
17
|
*/
|
|
12
|
-
export declare function runResultsPipeline(store: JobStore, job: JobMetadata, outputFormat: OutputFormat, includeRaw?: boolean): PipelineResult;
|
|
18
|
+
export declare function runResultsPipeline(store: JobStore, job: JobMetadata, outputFormat: OutputFormat, includeRaw?: boolean, opts?: PipelineOptions): PipelineResult;
|
|
13
19
|
//# sourceMappingURL=results-pipeline.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"results-pipeline.d.ts","sourceRoot":"","sources":["../../src/core/results-pipeline.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,WAAW,
|
|
1
|
+
{"version":3,"file":"results-pipeline.d.ts","sourceRoot":"","sources":["../../src/core/results-pipeline.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EACR,YAAY,EAEZ,iBAAiB,EACjB,iBAAiB,EAGlB,MAAM,aAAa,CAAA;AAEpB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAC9C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAG9C,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,iBAAiB,CAAA;IAC1B,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,QAAQ,CAAA;CACpB;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,QAAQ,GAAG,OAAO,CAG1F;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,QAAQ,GAAG,OAAO,CAG1F;AAkCD;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,QAAQ,EACf,GAAG,EAAE,WAAW,EAChB,YAAY,EAAE,YAAY,EAC1B,UAAU,UAAQ,EAClB,IAAI,GAAE,eAAoB,GACzB,cAAc,CAsKhB"}
|
|
@@ -4,6 +4,15 @@ import { formatJson } from '../formatters/json.js';
|
|
|
4
4
|
import { formatText } from '../formatters/text.js';
|
|
5
5
|
import { formatMarkdown } from '../formatters/markdown.js';
|
|
6
6
|
import { SEVERITY_ORDER } from '../types.js';
|
|
7
|
+
import { normalizeLocationForKey } from './stable-id.js';
|
|
8
|
+
export function isBlockingFinding(finding, threshold) {
|
|
9
|
+
return finding.acknowledged !== true &&
|
|
10
|
+
SEVERITY_ORDER[finding.severity] <= SEVERITY_ORDER[threshold];
|
|
11
|
+
}
|
|
12
|
+
export function isAdvisoryFinding(finding, threshold) {
|
|
13
|
+
return finding.acknowledged === true ||
|
|
14
|
+
SEVERITY_ORDER[finding.severity] > SEVERITY_ORDER[threshold];
|
|
15
|
+
}
|
|
7
16
|
/** Maximum chars of channel-log detail to embed in the per-channel error
|
|
8
17
|
* field. Keeps JSON output readable while preserving the head of any
|
|
9
18
|
* stderr / spawn-error message captured by the dispatcher. */
|
|
@@ -36,7 +45,7 @@ function appendLogDetail(baseMsg, store, jobId, channel) {
|
|
|
36
45
|
* Run the full results pipeline: parse channel outputs, reconcile findings,
|
|
37
46
|
* derive verdict, format output.
|
|
38
47
|
*/
|
|
39
|
-
export function runResultsPipeline(store, job, outputFormat, includeRaw = false) {
|
|
48
|
+
export function runResultsPipeline(store, job, outputFormat, includeRaw = false, opts = {}) {
|
|
40
49
|
const channelFindings = {};
|
|
41
50
|
const perChannel = {};
|
|
42
51
|
const startTimes = [];
|
|
@@ -99,6 +108,36 @@ export function runResultsPipeline(store, job, outputFormat, includeRaw = false)
|
|
|
99
108
|
};
|
|
100
109
|
}
|
|
101
110
|
const reconciledFindings = reconcile(channelFindings);
|
|
111
|
+
// Apply ack lookup (T2-D): stamp acknowledged/ack_match/ack_reason on matched
|
|
112
|
+
// findings, preserving agreement/confidence/sources. isBlockingFinding and
|
|
113
|
+
// isAdvisoryFinding already treat acknowledged findings as advisory-only, so
|
|
114
|
+
// the gate (evaluateGate) skips them when computing the verdict.
|
|
115
|
+
if (opts.ackStore) {
|
|
116
|
+
try {
|
|
117
|
+
for (const f of reconciledFindings) {
|
|
118
|
+
// Only finding_key is required: AckStore.lookup's exact path is
|
|
119
|
+
// key-only; the fuzzy fallback early-returns on an empty shingle.
|
|
120
|
+
if (f.finding_key === undefined)
|
|
121
|
+
continue;
|
|
122
|
+
const match = opts.ackStore.lookup({
|
|
123
|
+
finding_key: f.finding_key,
|
|
124
|
+
normalized_location: normalizeLocationForKey(f.location),
|
|
125
|
+
shingle: f.description_shingle ?? [],
|
|
126
|
+
});
|
|
127
|
+
if (match) {
|
|
128
|
+
f.acknowledged = true;
|
|
129
|
+
f.ack_match = match.match;
|
|
130
|
+
if (match.record.reason !== undefined)
|
|
131
|
+
f.ack_reason = match.record.reason;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
// Fail safe: if the ack store can't be read (e.g. a poisoned or
|
|
137
|
+
// symlinked .mmr/acks tree makes lookup throw), apply no suppression.
|
|
138
|
+
// Findings stay blocking, which is the safe direction for a gate.
|
|
139
|
+
}
|
|
140
|
+
}
|
|
102
141
|
const fixThreshold = job.fix_threshold;
|
|
103
142
|
const completedChannels = Object.values(job.channels)
|
|
104
143
|
.filter((ch) => ch.status === 'completed').length;
|
|
@@ -116,10 +155,10 @@ export function runResultsPipeline(store, job, outputFormat, includeRaw = false)
|
|
|
116
155
|
: verdict === 'needs-user-decision'
|
|
117
156
|
? 'No channels completed — manual review needed'
|
|
118
157
|
: (() => {
|
|
119
|
-
const blockingCount = reconciledFindings.filter((f) =>
|
|
158
|
+
const blockingCount = reconciledFindings.filter((f) => isBlockingFinding(f, fixThreshold)).length;
|
|
120
159
|
return `Review blocked — ${blockingCount} finding(s) at or above ${fixThreshold}`;
|
|
121
160
|
})();
|
|
122
|
-
const advisoryCount = reconciledFindings.filter((f) =>
|
|
161
|
+
const advisoryCount = reconciledFindings.filter((f) => isAdvisoryFinding(f, fixThreshold)).length;
|
|
123
162
|
const results = {
|
|
124
163
|
job_id: job.job_id,
|
|
125
164
|
verdict,
|
|
@@ -137,6 +176,14 @@ export function runResultsPipeline(store, job, outputFormat, includeRaw = false)
|
|
|
137
176
|
total_elapsed: totalElapsed,
|
|
138
177
|
},
|
|
139
178
|
};
|
|
179
|
+
// Re-surface trust context persisted on the job at review time (§5 decision
|
|
180
|
+
// 1), so review --sync, `mmr results`, and `mmr reconcile` all carry it.
|
|
181
|
+
if (job.trust_mode !== undefined)
|
|
182
|
+
results.trust_mode = job.trust_mode;
|
|
183
|
+
if (job.proposed_acks !== undefined)
|
|
184
|
+
results.proposed_acks = job.proposed_acks;
|
|
185
|
+
if (job.proposed_config_change !== undefined)
|
|
186
|
+
results.proposed_config_change = job.proposed_config_change;
|
|
140
187
|
let formatted;
|
|
141
188
|
switch (outputFormat) {
|
|
142
189
|
case 'text':
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"results-pipeline.js","sourceRoot":"","sources":["../../src/core/results-pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACxE,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;
|
|
1
|
+
{"version":3,"file":"results-pipeline.js","sourceRoot":"","sources":["../../src/core/results-pipeline.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface ProbeResult {
|
|
2
|
+
detected: boolean;
|
|
3
|
+
reason?: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Probe for a local runtime by running `<command> <args>` with the given
|
|
7
|
+
* timeout (ms). Returns detected=true if the process exits 0 within the
|
|
8
|
+
* timeout.
|
|
9
|
+
*
|
|
10
|
+
* The command name is validated against a strict character set before
|
|
11
|
+
* spawn to prevent shell injection from a hardcoded probe list.
|
|
12
|
+
*/
|
|
13
|
+
export declare function probeRuntime(command: string, args: string[], timeoutMs: number): Promise<ProbeResult>;
|
|
14
|
+
//# sourceMappingURL=runtime-probe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-probe.d.ts","sourceRoot":"","sources":["../../src/core/runtime-probe.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,OAAO,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAID;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,CAAC,CA+CtB"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
const MAX_TIMEOUT_MS = 2_147_483_647;
|
|
3
|
+
/**
|
|
4
|
+
* Probe for a local runtime by running `<command> <args>` with the given
|
|
5
|
+
* timeout (ms). Returns detected=true if the process exits 0 within the
|
|
6
|
+
* timeout.
|
|
7
|
+
*
|
|
8
|
+
* The command name is validated against a strict character set before
|
|
9
|
+
* spawn to prevent shell injection from a hardcoded probe list.
|
|
10
|
+
*/
|
|
11
|
+
export async function probeRuntime(command, args, timeoutMs) {
|
|
12
|
+
if (!/^[a-zA-Z0-9._/\\: ()@+~-]+$/.test(command)) {
|
|
13
|
+
return { detected: false, reason: 'invalid command name' };
|
|
14
|
+
}
|
|
15
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > MAX_TIMEOUT_MS) {
|
|
16
|
+
return { detected: false, reason: 'invalid timeout' };
|
|
17
|
+
}
|
|
18
|
+
for (const arg of args) {
|
|
19
|
+
if (arg.includes('\0')) {
|
|
20
|
+
return { detected: false, reason: 'invalid argument' };
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return new Promise((resolve) => {
|
|
24
|
+
let settled = false;
|
|
25
|
+
let timedOut = false;
|
|
26
|
+
let killTimer;
|
|
27
|
+
const child = spawn(command, args, { stdio: 'ignore' });
|
|
28
|
+
const timer = setTimeout(() => {
|
|
29
|
+
timedOut = true;
|
|
30
|
+
child.kill('SIGTERM');
|
|
31
|
+
killTimer = setTimeout(() => {
|
|
32
|
+
child.kill('SIGKILL');
|
|
33
|
+
}, 250);
|
|
34
|
+
}, timeoutMs);
|
|
35
|
+
function finish(result) {
|
|
36
|
+
if (settled)
|
|
37
|
+
return;
|
|
38
|
+
settled = true;
|
|
39
|
+
clearTimeout(timer);
|
|
40
|
+
if (killTimer)
|
|
41
|
+
clearTimeout(killTimer);
|
|
42
|
+
resolve(result);
|
|
43
|
+
}
|
|
44
|
+
child.on('close', (code, signal) => {
|
|
45
|
+
if (timedOut) {
|
|
46
|
+
finish({ detected: false, reason: 'timeout' });
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const reason = code === 0 ? undefined : signal ? `signal ${signal}` : `exit ${code}`;
|
|
50
|
+
finish({ detected: code === 0, reason });
|
|
51
|
+
});
|
|
52
|
+
child.on('error', (err) => {
|
|
53
|
+
finish({ detected: false, reason: err.message });
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=runtime-probe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-probe.js","sourceRoot":"","sources":["../../src/core/runtime-probe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAO1C,MAAM,cAAc,GAAG,aAAa,CAAA;AAEpC;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAe,EACf,IAAc,EACd,SAAiB;IAEjB,IAAI,CAAC,6BAA6B,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACjD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAA;IAC5D,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,SAAS,GAAG,CAAC,IAAI,SAAS,GAAG,cAAc,EAAE,CAAC;QAChF,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAA;IACvD,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACvB,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAA;QACxD,CAAC;IACH,CAAC;IAED,OAAO,IAAI,OAAO,CAAc,CAAC,OAAO,EAAE,EAAE;QAC1C,IAAI,OAAO,GAAG,KAAK,CAAA;QACnB,IAAI,QAAQ,GAAG,KAAK,CAAA;QACpB,IAAI,SAAoD,CAAA;QACxD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAA;QAEvD,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,QAAQ,GAAG,IAAI,CAAA;YACf,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;YACrB,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC1B,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;YACvB,CAAC,EAAE,GAAG,CAAC,CAAA;QACT,CAAC,EAAE,SAAS,CAAC,CAAA;QAEb,SAAS,MAAM,CAAC,MAAmB;YACjC,IAAI,OAAO;gBAAE,OAAM;YACnB,OAAO,GAAG,IAAI,CAAA;YACd,YAAY,CAAC,KAAK,CAAC,CAAA;YACnB,IAAI,SAAS;gBAAE,YAAY,CAAC,SAAS,CAAC,CAAA;YACtC,OAAO,CAAC,MAAM,CAAC,CAAA;QACjB,CAAC;QAED,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;YACjC,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAA;gBAC9C,OAAM;YACR,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,MAAM,EAAE,CAAC,CAAC,CAAC,QAAQ,IAAI,EAAE,CAAA;YACpF,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,CAAA;QAC1C,CAAC,CAAC,CAAA;QACF,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACxB,MAAM,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAA;QAClD,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Finding } from '../types.js';
|
|
2
|
+
export declare function normalizeLocationForKey(location: string): string;
|
|
3
|
+
export declare function normalizeDescriptionForKey(description: string): string;
|
|
4
|
+
export declare function normalizeSuggestionForKey(suggestion: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Compute the stable identity key per §5 decision 2:
|
|
7
|
+
* finding_key = sha1(
|
|
8
|
+
* normalized_location + "|" + (category ?? "") + "|" +
|
|
9
|
+
* sha1(description_normalized) + "|" + sha1(suggestion_normalized)
|
|
10
|
+
* )
|
|
11
|
+
*
|
|
12
|
+
* Severity is intentionally excluded — the same underlying issue surfacing at
|
|
13
|
+
* P1 vs P2 across channels should still reconcile to one key.
|
|
14
|
+
*/
|
|
15
|
+
export declare function computeFindingKey(finding: Finding): string;
|
|
16
|
+
export declare function descriptionShingle(description: string): string[];
|
|
17
|
+
export declare function jaccardSimilarity(a: readonly string[] | ReadonlySet<string>, b: readonly string[] | ReadonlySet<string>): number;
|
|
18
|
+
export declare function shingleSize(shingle: readonly string[] | ReadonlySet<string>): number;
|
|
19
|
+
//# sourceMappingURL=stable-id.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stable-id.d.ts","sourceRoot":"","sources":["../../src/core/stable-id.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAY1C,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEhE;AAwCD,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEtE;AAQD,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAIpE;AAmCD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAM1D;AAMD,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAUhE;AAED,wBAAgB,iBAAiB,CAC/B,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,EAC1C,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,GACzC,MAAM,CAWR;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,GAAG,MAAM,CAEpF"}
|