import { describe, expect, it, vi } from 'vitest'; import { RegulatoryFindingRetrievalService } from '../../server/compliance/regulatory-finding-retrieval'; import type { RegulatoryRetrievalCandidate, RegulatoryRetrievalRequest } from '../../src/compliance/regulatory-rag'; import type { UnifiedResultProjection } from '../../src/domain/unified-result'; import { buildRegulatoryFindingQuery } from '../../src/compliance/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('names Purple Knight by the scoped tool the Result DB delivers and keeps its public indicator ID', () => { const query = buildRegulatoryFindingQuery(finding({ sourceTool: 'purple-knight-ad', sourceRuleId: 'SI000077' })); expect(query).toMatchObject({ specificity: 'rule', publicTool: 'Semperis Purple Knight for Active Directory', publicRuleId: 'SI000077' }); expect(query?.query).toContain('Public rule ID: SI000077.'); expect(buildRegulatoryFindingQuery(finding({ sourceTool: 'purple-knight-entra', sourceRuleId: 'SI-custom' }))) .toMatchObject({ specificity: 'topic', publicTool: 'Semperis Purple Knight for Entra ID' }); }); it('maps a GPO baseline comparison from its disclosed setting text and never from the customer value', () => { const query = buildRegulatoryFindingQuery(finding({ resultProjectionId: 'policy-evaluation-comparison:synthetic', sourceKind: 'policy-evaluation', sourceTool: 'GPOs', sourceRuleId: 'registry:machine:software\policies\synthetic:mode', title: 'Synthetic policy mode', description: 'Controls the synthetic SMB signing mode.', recommendation: 'Enable the synthetic policy mode.', policyBaselineLabel: 'DUMMY Microsoft Security Baseline Windows Server 2022 v1', policyBaselineValue: { operation: 'set-value', data: { kind: 'dword', value: 1 } }, policyActualValue: { operation: 'set-value', data: { kind: 'dword', value: 0 }, principals: ['DUMMY\named.user'] } })); expect(query).toMatchObject({ specificity: 'topic', publicTool: 'Microsoft Group Policy' }); expect(query?.query).toContain('Synthetic policy mode'); expect(query?.publicRuleId).toBeUndefined(); expect(query?.query).not.toMatch(/named.user|DUMMY|policyActualValue/); }); it('fails closed only for context records or findings without a check title', () => { expect(buildRegulatoryFindingQuery(finding({ sourceKind: 'policy-observation' }))).toBeUndefined(); 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(); }); }); describe('GPO baseline context in the regulatory query', () => { const gpo = (overrides: Partial = {}) => finding({ resultProjectionId: 'policy-evaluation-comparison:one', sourceKind: 'policy-evaluation', sourceTool: 'GPOs', sourceRuleId: 'gpo-policy-identity-0123456789abcdef', title: 'System Access · MinimumPasswordLength', description: undefined, recommendation: undefined, affectedAsset: undefined, policyScope: 'computer', policyDeliveryMechanism: 'security-template', policyCustomerState: 'configured', policyComparisonRelation: 'different', policyBaselineLabel: 'Microsoft Security Baseline - Windows Server 2025 v2506', policyBaselineValue: { kind: 'integer', value: '14' }, // The customer value is a synthetic secret that must never reach the query. policyActualValue: { kind: 'integer', value: '7' }, ...overrides }); it('adds section, readable name, baseline family and value and the proven difference, never the configured value', () => { const query = buildRegulatoryFindingQuery(gpo())!.query; expect(query).toContain('Group Policy setting type: Computer Configuration, security template (GptTmpl.inf).'); expect(query).toContain('Policy section: System Access.'); expect(query).toContain('Setting name: Minimum Password Length.'); // Only the baseline family crosses, never the concrete release. expect(query).toContain('Baseline: Microsoft Security Baseline - Windows Server.'); expect(query).not.toContain('v2506'); expect(query).toContain('Baseline recommended value: 14.'); // An exact comparison proves a difference, not a direction. expect(query).toContain('The configured value differs from the baseline recommendation.'); expect(query).not.toMatch(/below|weaker|lower/i); expect(query).not.toMatch(/\b7\b/); expect(buildRegulatoryFindingQuery(gpo())!.publicTopics).toContain('Password policy'); }); it('states a missing setting and a recorded weaker comparison as such', () => { expect(buildRegulatoryFindingQuery(gpo({ policyCustomerState: 'missing', policyActualValue: undefined }))!.query) .toContain('The recommended setting is not configured in the analysed Group Policy backups.'); const weaker = gpo({ policyComparisonRelation: 'weaker', policyBaselineVariantAssessment: undefined }); expect(buildRegulatoryFindingQuery(weaker)!.query).toContain('The configured value is weaker than the baseline recommendation.'); }); it('adds the documented meaning of a user right and a well-known SID, never resolving a domain SID', () => { const query = buildRegulatoryFindingQuery(gpo({ title: 'Privilege Rights · SeBackupPrivilege', policyBaselineValue: { kind: 'string-set', values: ['*S-1-5-32-544', '*S-1-5-32-551'] }, policyActualValue: { kind: 'string-set', values: ['*S-1-5-21-1111111111-2222222222-3333333333-1105'] } }))!.query; expect(query).toContain('Setting name: Se Backup Privilege (Back up files and directories).'); expect(query).toContain('Baseline recommended value: *S-1-5-32-544 (Administrators), *S-1-5-32-551 (Backup Operators).'); expect(query).not.toContain('1105'); // A setting without a documented code keeps its plain readable name. expect(buildRegulatoryFindingQuery(gpo())!.query).toContain('Setting name: Minimum Password Length.'); }); it('names a registry key and leaves scanner queries unchanged', () => { const registry = buildRegulatoryFindingQuery(gpo({ policyDeliveryMechanism: 'registry-policy', title: 'Software\Policies\Microsoft\Windows\System · EnableSmartScreen', policyBaselineValue: { kind: 'integer', value: '1' } }))!.query; expect(registry).toContain('Registry key: Software\Policies\Microsoft\Windows\System.'); expect(registry).toContain('Setting name: Enable Smart Screen.'); expect(buildRegulatoryFindingQuery(finding())!.query).not.toContain('Group Policy setting type'); }); });