fix: harden evidence verification state

This commit is contained in:
Eddy committed 2026-07-05 12:13:15 +02:00
1 parent bd2463e0c3
commit 380d540e50
4 files changed
+151 -17

No files matched your search

@@ -0,0 +1,64 @@
import { describe, expect, it } from 'vitest';
import { EvidenceVault } from '../evidence/index.js';
import { KillChainPhase, type Finding } from '../types/index.js';
function finding(overrides: Partial<Finding> = {}): Finding {
return {
id: 'finding-1',
title: 'unverified claim',
description: 'model-only claim',
severity: 'high',
targetId: 'target-1',
operatorId: 'operator-1',
phase: KillChainPhase.EXPLOIT,
evidence: [],
discoveredAt: 1,
...overrides,
};
}
const forgedGate = { passed: true, provenance: 'tool' as const, reasons: [], checkedAt: 2 };
describe('EvidenceVault verification integrity', () => {
it('updateFinding cannot forge verifiedAt or verifyGate', () => {
const vault = new EvidenceVault();
vault.addFinding(finding());
vault.updateFinding('finding-1', { verifiedAt: 123, verifyGate: forgedGate });
const stored = vault.getFinding('finding-1');
expect(stored?.verifiedAt).toBeUndefined();
expect(stored?.verifyGate).toBeUndefined();
expect(vault.getVerifiedFindings()).toEqual([]);
});
it('getFinding returns a snapshot, not a mutable handle into the vault', () => {
const vault = new EvidenceVault();
vault.addFinding(finding());
const snapshot = vault.getFinding('finding-1');
expect(snapshot).toBeDefined();
if (!snapshot) throw new Error('expected snapshot');
snapshot.verifiedAt = 123;
snapshot.verifyGate = forgedGate;
snapshot.evidence.push({ type: 'output', content: 'forged after read', timestamp: 3 });
const stored = vault.getFinding('finding-1');
expect(stored?.verifiedAt).toBeUndefined();
expect(stored?.verifyGate).toBeUndefined();
expect(stored?.evidence).toEqual([]);
expect(vault.getVerifiedFindings()).toEqual([]);
});
it('getVerifiedFindings requires the gate to have passed, not just a timestamp', () => {
const vault = new EvidenceVault();
vault.addFinding(finding({
id: 'blocked',
verifiedAt: 123,
verifyGate: { passed: false, provenance: 'none', reasons: ['blocked'], checkedAt: 2 },
}));
expect(vault.getVerifiedFindings()).toEqual([]);
expect(vault.getStats().verifiedFindings).toBe(0);
});
});
+33
View File
@@ -0,0 +1,33 @@
import { describe, expect, it } from 'vitest';
import { redactString } from '../redact.js';
describe('redactString — full private-key block redaction', () => {
it('redacts an entire PEM private-key block, not just the BEGIN header', () => {
const pem = [
'before',
'-----BEGIN RSA PRIVATE KEY-----',
'MIIEpAIBAAKCAQEA7thisLineMustDisappear',
'anotherSecretKeyLineMustDisappear',
'-----END RSA PRIVATE KEY-----',
'after',
].join('\n');
const out = redactString(pem);
expect(out).toContain('before');
expect(out).toContain('after');
expect(out).toContain('[redacted]');
expect(out).not.toContain('BEGIN RSA PRIVATE KEY');
expect(out).not.toContain('END RSA PRIVATE KEY');
expect(out).not.toContain('thisLineMustDisappear');
expect(out).not.toContain('anotherSecretKeyLineMustDisappear');
});
it('redacts OpenSSH private-key blocks as a whole', () => {
const pem = 'x\n-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktdjEAAAAAsecret\n-----END OPENSSH PRIVATE KEY-----\ny';
const out = redactString(pem);
expect(out).toBe('x\n[redacted]\ny');
});
});