diff --git a/dist/diff.js b/dist/diff.js index 1502b99..e885738 100644 Binary files a/dist/diff.js and b/dist/diff.js differ diff --git a/src/diff.ts b/src/diff.ts index 7b693bf..e2a148f 100644 Binary files a/src/diff.ts and b/src/diff.ts differ diff --git a/test/heuristics.test.mjs b/test/heuristics.test.mjs index e302557..e1c69da 100644 --- a/test/heuristics.test.mjs +++ b/test/heuristics.test.mjs @@ -340,6 +340,41 @@ test('diffReports: omits resolvedFindings field when nothing was resolved', () = assert.equal(delta.resolvedFindings, undefined); }); +test('diffReports: same kind/subject/file and severity but different message is surfaced as [CHANGED]', () => { + // A medium finding mutated into a *different* medium at the same + // place. The (kind, subject, file) key matches, so without a content + // check this would be dropped as pre-existing. + const base = makeReport([makeFinding({ message: 'github launches npx server-github.' })]); + const head = makeReport([makeFinding({ message: 'github launches npx server-evil.' })]); + + const delta = diffReports(base, head); + assert.equal(delta.findingCount, 1); + assert.equal(delta.findings[0].severity, 'medium'); + assert.match(delta.findings[0].message, /^\[CHANGED\]/); + assert.match(delta.findings[0].message, /server-evil/); +}); + +test('diffReports: same severity with a changed signature is surfaced as [CHANGED]', () => { + // When reports carry signatures (the audit path always signs), the + // content identity comes from the signature rather than the message. + const base = makeReport([makeFinding({ signature: 'aaaaaaaaaaaaaaaa' })]); + const head = makeReport([makeFinding({ signature: 'bbbbbbbbbbbbbbbb' })]); + + const delta = diffReports(base, head); + assert.equal(delta.findingCount, 1); + assert.match(delta.findings[0].message, /^\[CHANGED\]/); +}); + +test('diffReports: same severity and identical signature stays out of the delta', () => { + // Identical signatures mean the violation is unchanged even if some + // unrelated report field differs — it is pre-existing, not a regression. + const base = makeReport([makeFinding({ signature: 'cafef00dcafef00d', message: 'phrasing A' })]); + const head = makeReport([makeFinding({ signature: 'cafef00dcafef00d', message: 'phrasing B' })]); + + const delta = diffReports(base, head); + assert.equal(delta.findingCount, 0); +}); + test('makeMeshContext: indexes servers by name across surfaces in a single pass', () => { const policies = { mcpSurfaces: [