import { createElement } from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; import { describe, expect, it } from 'vitest'; import type { FindingRecord } from '../../src/domain/finding'; import { findingWebSearch } from '../../src/profiles/public-search/search'; import catalog from '../../src/profiles/public-search/catalog.json'; import { FindingReader } from '../../src/features/finding-reader/FindingReader'; function record(title: string, values: Record = {}): FindingRecord { return { id: 'synthetic', title, index: 0, filled: [], meta: [], fields: Object.entries(values).map(([label, value], index) => ({ label, value, index })) }; } describe('tool-independent public AI search', () => { it('resolves native policy identity, location and bounded benchmark target', () => { const result = findingWebSearch(record('system access · minimumpasswordlength', { 'Baseline value': '{"kind":"number","value":14}', Scope: 'computer', Baseline: 'Windows Server 2022 Security Baseline', Status: 'Fail' }), 'group-policy'); expect(result.specificity).toBe('setting'); expect(result.query).toContain('MinimumPasswordLength'); expect(result.query).toContain('Account Policies'); expect(result.query).toContain('Selected benchmark target: 14'); expect(result.query).toContain('Windows Server 2022'); }); it('resolves public rights without sending assigned identities', () => { const result = findingWebSearch(record('privilege rights · sebackupprivilege', { 'Baseline value': '["S-1-5-21-123-456-789-1001"]', 'Configured Value': 'SYNTHETIC\\PrivateUser' }), 'group-policy'); expect(result.query).toContain('SeBackupPrivilege'); expect(result.query).toContain('User Rights Assignment'); expect(result.query).not.toContain('S-1-5-21'); expect(result.query).not.toContain('PrivateUser'); }); it('matches full public registry targets and refuses private extensions', () => { const path = 'HKEY_LOCAL_MACHINE\\SOFTWARE\\Policies\\Microsoft\\Windows Defender'; const result = findingWebSearch(record('PUAProtection', { 'Registry path': path, 'Baseline value': '1' }), 'group-policy'); expect(result.specificity).toBe('setting'); expect(result.query).toContain('Public registry target: SOFTWARE\\Policies\\Microsoft\\Windows Defender\\PUAProtection'); expect(result.query).toContain('not the selected SCT baseline'); const privateResult = findingWebSearch(record('PUAProtection', { 'Registry path': path + '\\PrivateTenant' }), 'group-policy'); expect(privateResult.query).not.toContain('Public registry target:'); expect(privateResult.query).not.toContain('PrivateTenant'); }); it('resolves ASR GUIDs only within the supported registry family', () => { const id = '5beb7efe-fd9a-4556-801d-275e5ffc04cc'; const result = findingWebSearch(record(id, { 'Registry path': 'SOFTWARE\\Policies\\Microsoft\\Windows Defender\\Windows Defender Exploit Guard\\ASR\\Rules' }), 'group-policy'); expect(result.query).toContain(id); expect(result.query).toContain('Attack Surface Reduction'); expect(findingWebSearch(record(id), 'group-policy').query).not.toContain(id); }); it.each(['maester', 'pingcastle'])('uses verified public IDs for %s without dynamic instance content', tool => { const id = tool === 'maester' ? 'MT.1006' : 'A-AdminSDHolder'; const result = findingWebSearch(record(`${id}: private-user@synthetic.invalid`, { Description: 'PrivateTenant' }), tool); expect(result.specificity).toBe('rule'); expect(result.query).toContain(`Public rule ID: ${id}`); expect(result.query).not.toContain('private-user'); expect(result.query).not.toContain('PrivateTenant'); }); it('resolves the public CISA Exchange audit-retention rule without calling it Windows audit policy', () => { const result = findingWebSearch(record( 'Audit logs SHALL be maintained for at least the minimum duration dictated by OMB M-21-31 (Appendix C).', { ID: 'CISA.MS.EXO.17.3', Status: 'Failed', Tag: '["MS.EXO","CISA.MS.EXO.17.3"]' } ), 'maester'); expect(result.specificity).toBe('rule'); expect(result.query).toContain('Public rule ID: CISA.MS.EXO.17.3'); expect(result.query).toContain('https://maester.dev/docs/tests/CISA.MS.EXO.17.3/'); expect(result.query).toContain('Microsoft Purview audit log retention'); expect(result.query).not.toContain('Windows audit policy'); expect(result.query).not.toContain('exact public rule is not resolved'); }); it.each(['cis-cat', 'purple-knight', 'generic'])('adds useful fixed technical concepts for %s', tool => { const result = findingWebSearch(record('SMB signing on private-host.synthetic.invalid', { Description: 'SMB signing is required for PrivateTenant', Benchmark: 'Windows Server 2022 PrivateTenant' }), tool); expect(result.query).toContain('SMB signing requirements'); expect(result.query).toContain('Windows Server 2022'); expect(result.query).not.toContain('private-host'); expect(result.query).not.toContain('PrivateTenant'); }); it('supports a mixed-tool workspace and labels unresolved rules honestly', () => { const mixed = record('MT.1006', { 'Source: Scan': 'Maester · Synthetic' }); expect(findingWebSearch(mixed, 'generic').query).toContain('Maester'); const result = findingWebSearch(record('Unknown private rule', { Description: 'person@example.invalid' }), 'cis-cat'); expect(result.specificity).toBe('topic'); expect(result.query).toContain('exact public rule is not resolved'); expect(result.query).not.toContain('person@'); }); it('never forwards free text, raw descriptions, source URLs, paths or customer values', () => { const secret = 'Synthetic Person private@example.invalid 10.20.30.40 \\\\private\\share https://private.invalid'; const item = record(secret, Object.fromEntries(['Description', 'Remediation', 'Configured Value', 'Sources', 'Owner', 'Baseline value', 'Registry path', 'Rule ID', 'Search URL'].map(k => [k, secret]))); for (const tool of ['group-policy', 'cis-cat', 'maester', 'pingcastle', 'purple-knight', 'generic']) { const result = findingWebSearch(item, tool); expect(result.query).not.toMatch(/Synthetic Person|private@|10\.20|private\\|private\.invalid/); expect(new URL(result.url).searchParams.get('q')).toBe(result.query); expect(new URL(result.url).searchParams.get('udm')).toBe('50'); } }); it('rejects malformed, string, unbounded or ambiguous benchmark values', () => { for (const value of ['"private-person"', '99999999', '{"kind":"number","value":1,"secret":"private"}', '1 private']) { const item = record('system access · MinimumPasswordLength', { 'Baseline value': value }); expect(findingWebSearch(item, 'group-policy').query).not.toContain('Selected benchmark target:'); } const item = record('system access · MinimumPasswordLength', { 'Baseline value': '14' }); item.fields.push({ label: 'Baseline value', value: '12', index: 1 }); expect(findingWebSearch(item, 'group-policy').query).not.toContain('Selected benchmark target:'); }); it.each(['cis-cat', 'purple-knight', 'maester', 'pingcastle', 'group-policy', 'generic'])('renders an explicit protected search link for %s', toolId => { const item = record('SMB signing'); const html = renderToStaticMarkup(createElement(FindingReader, { record: item, title: item.title, toolId, linkedSheets: [], resultReferences: [], search: '', position: 0, recordCount: 1, onPrevious() {}, onNext() {}, onReview() {} })); expect(html).toContain('>AI search'); expect(html).toContain('rel="noopener noreferrer"'); expect(html).toContain('referrerPolicy="no-referrer"'); expect(html).toContain('data-search-specificity='); }); it('keeps a source-attributed public identifier catalog without report prose', () => { expect(catalog.registryTargets.length).toBeGreaterThan(60); expect(catalog.privilegeIds.length).toBe(45); expect(catalog.maesterIds.length).toBeGreaterThan(150); expect(catalog.pingcastleIds.length).toBeGreaterThan(40); expect(catalog.asrIds).toContain('5beb7efe-fd9a-4556-801d-275e5ffc04cc'); for (const list of [catalog.registryTargets, catalog.privilegeIds, catalog.maesterIds, catalog.pingcastleIds, catalog.asrIds]) expect(new Set(list).size).toBe(list.length); }); });