import { describe, expect, it, vi } from 'vitest'; import { RegulatoryFindingRetrievalService } from '../../server/regulatory/regulatory-finding-retrieval'; import type { RegulatoryRetrievalCandidate, RegulatoryRetrievalRequest } from '../../src/domain/regulatory-rag'; import type { UnifiedResultProjection } from '../../src/domain/unified-result'; import { buildRegulatoryFindingQuery } from '../../src/profiles/public-search/regulatory-query'; // Result-table / occurrence data and raw addresses that must never reach the query. const resultTableAsset = 'DC01.customer.local'; const reporterEmail = 'ops@acme.invalid'; const ownerEmail = 'analyst@acme.invalid'; function finding(overrides: Partial = {}): UnifiedResultProjection { return { resultProjectionId: 'projection-1', sourceKind: 'scanner-finding', sourceResultId: 'source-1', projectId: 'tenant-1', assessmentId: 'assessment-1', collectionRunId: 'run-1', sourceTool: 'purple-knight', title: 'SMB signing is not required on domain controllers', description: `The SMB server permits unsigned sessions, allowing relay attacks. Reported by ${reporterEmail}.`, recommendation: 'Require SMB signing through Group Policy on all domain controllers.', affectedAsset: resultTableAsset, severityOriginal: 'high', severityNormalized: 'high', outcomeOriginal: 'fail', outcomeNormalized: 'fail', technicalExecutionState: 'completed', resultType: 'compliance_check', isIoe: false, isDeviation: true, lifecycleState: 'new', mappingConfidence: 1, reviewStatus: 'open', reviewOwner: ownerEmail, importedAt: '2026-09-15T10:00:00.000Z', createdAt: '2026-09-15T10:00:00.000Z', frameworkReferences: [], categories: [], ...overrides }; } const candidate: RegulatoryRetrievalCandidate = { score: 0.8, chunk: { schemaVersion: 1, corpusId: 'synthetic', corpusVersion: '1', documentId: 'control-de', framework: 'Synthetic Framework', frameworkVersion: '1', controlId: 'CTRL-1', chunkId: 'chunk-1', chunkIndex: 0, language: 'de', title: 'Synthetic control', text: 'Synthetic regulatory text.', textSha256: 'a'.repeat(64) } }; describe('regulatory finding retrieval boundary', () => { it('builds the query from the non-personal check text and never from the result table', () => { const query = buildRegulatoryFindingQuery(finding()); expect(query).toMatchObject({ schemaVersion: 1, specificity: 'topic', publicTool: 'Semperis Purple Knight', publicTopics: ['SMB signing requirements'] }); // The rule-level check text is the intended source. expect(query?.query).toContain('SMB signing is not required on domain controllers'); expect(query?.query).toContain('unsigned sessions'); // Result-table / occurrence data and raw email addresses never appear. expect(query?.query).not.toContain(resultTableAsset); expect(query?.query).not.toContain(reporterEmail); expect(query?.query).not.toContain(ownerEmail); expect(query?.query).toContain('[redacted-email]'); }); it('maps every scanner finding, sharpening with a catalog rule ID when available', () => { expect(buildRegulatoryFindingQuery(finding({ sourceTool: 'pingcastle', sourceRuleId: 'a-krbtgt' }))).toMatchObject({ specificity: 'rule', publicRuleId: 'A-Krbtgt' }); // Unknown tool, non-catalog rule ID, no topic match: still mapped from the check title. const generic = buildRegulatoryFindingQuery(finding({ sourceTool: 'customer-private-scanner', sourceRuleId: 'A-Custom', title: 'Weak service account configuration', description: undefined, recommendation: undefined })); expect(generic).toMatchObject({ specificity: 'topic' }); expect(generic?.query).toContain('Weak service account configuration'); expect(generic?.publicRuleId).toBeUndefined(); }); it('fails closed only for non-scanner records or findings without a check title', () => { expect(buildRegulatoryFindingQuery(finding({ sourceKind: 'environment-observation' }))).toBeUndefined(); expect(buildRegulatoryFindingQuery(finding({ title: ' ' }))).toBeUndefined(); }); it('passes only the prepared query and explicit filters to the retriever', async () => { const retrieve = vi.fn(async (_request: RegulatoryRetrievalRequest) => [candidate]); const service = new RegulatoryFindingRetrievalService({ retrieve }); const result = await service.retrieve(finding(), 5, { framework: 'Synthetic Framework' }); expect(result).toMatchObject({ sourceResultId: 'source-1', status: 'candidates', candidates: [candidate] }); expect(retrieve).toHaveBeenCalledOnce(); expect(retrieve.mock.calls[0][0]).toEqual({ query: result.preparedQuery?.query, limit: 5, filters: { framework: 'Synthetic Framework' } }); for (const marker of [resultTableAsset, reporterEmail, ownerEmail]) { expect(JSON.stringify(retrieve.mock.calls[0][0])).not.toContain(marker); } }); it('does not call retrieval for a finding without a check title', async () => { const retrieve = vi.fn(); const service = new RegulatoryFindingRetrievalService({ retrieve }); await expect(service.retrieve(finding({ title: '' }), 5)) .resolves.toEqual({ sourceResultId: 'source-1', status: 'insufficient-public-context', candidates: [] }); expect(retrieve).not.toHaveBeenCalled(); }); });