import { describe, expect, it } from 'vitest'; import type { UnifiedResultProjection } from '../../src/domain/unified-result'; import { discloseResultDbProjection, discloseResultDbProjectionWithTrace, removeResultDbScopeIdentifiers, RESULT_DB_DISCLOSURE_POLICY_VERSION } from '../../src/domain/result-db-disclosure'; function unsafeScanner(): UnifiedResultProjection { return { resultProjectionId: 'scanner-finding:private-occurrence', sourceKind: 'scanner-finding', sourceResultId: 'private-occurrence', projectId: 'private-company', assessmentId: 'private-assessment', collectionRunId: 'private-run', scanRunId: 'private-run', sourceTool: 'maester', sourceRuleId: 'MT.1001', sourceToolVersion: 'private-build', sourceProfileId: 'maester', canonicalDefinitionId: 'private-definition', importArtifactId: 'private-artifact', sourceViewId: 'private-view', sourceLocator: { objectPath: 'private/path' }, title: 'Kerberos preauthentication is disabled for person@private-company.example', description: 'Private Company GmbH tenant permits legacy authentication.', recommendation: 'Disable legacy authentication and contact Jane Doe.', affectedAsset: 'private-company.example', severityOriginal: 'Private severity', severityNormalized: 'high', outcomeOriginal: 'Private outcome', outcomeNormalized: 'fail', technicalExecutionState: 'completed', resultType: 'compliance_check', isIoe: false, isDeviation: true, lifecycleState: 'unknown', mappingConfidence: 1, reviewStatus: 'open', reviewOwner: 'Jane Doe', reviewDueDate: '2026-10-01', sourceObservedAt: '2026-09-22T08:00:00.000Z', importedAt: '2026-09-22T08:00:00.000Z', createdAt: '2026-09-22T08:00:00.000Z', frameworkReferences: [], categories: [{ namespace: 'private-company', categoryCode: 'private', categoryLabel: 'Private Company', categoryType: 'source', isPrimary: true }] }; } describe(`Result DB disclosure ${RESULT_DB_DISCLOSURE_POLICY_VERSION}`, () => { it('keeps useful known-scanner guidance while redacting customer identifiers', () => { const disclosed = discloseResultDbProjection(unsafeScanner()); expect(disclosed).toMatchObject({ title: 'Kerberos preauthentication is disabled for [email]', sourceTool: 'maester', sourceRuleId: 'MT.1001', description: '[organization] tenant permits legacy authentication.', recommendation: 'Disable legacy authentication and contact [redacted].', severityOriginal: 'high', outcomeOriginal: 'fail', importedAt: '1970-01-01T00:00:00.000Z', categories: [{ namespace: 'source-tool', categoryCode: 'maester', categoryLabel: 'Maester' }] }); const serialized = JSON.stringify(disclosed); for (const privateValue of ['person@private-company.example', 'Private Company GmbH', 'Jane Doe', 'private-company.example', 'private/path', 'private-build', 'private-definition', 'private-artifact']) { expect(serialized).not.toContain(privateValue); } }); it('withholds review status, disposition and priority from the Result DB projection', () => { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), reviewDisposition: 'accepted', reviewPriority: 'high' }); for (const key of ['reviewStatus', 'reviewDisposition', 'reviewPriority', 'reviewOwner', 'reviewDueDate', 'sourceToolVersion', 'sourceObservedAt', 'affectedAsset', 'sourceLocator']) { expect(Object.keys(disclosed)).not.toContain(key); } expect(discloseResultDbProjection(disclosed)).toEqual(disclosed); }); it('replaces environment fingerprints in scanner prose but keeps public technical tokens', () => { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceTool: 'pingcastle', sourceRuleId: 'A-NotEnoughDC', title: 'The number of DCs is too small to provide redundancy: 1 DC', description: 'Last AD backup 55 days ago on 2026-08-04 at 13:37; built-in admin used 0 day(s) ago. ' + '15 accounts and 1.015 objects, 3,250 computers, 42% of users, observed 28.09.2026.', recommendation: 'Disable NTLMv1 and TLS 1.0 on Windows Server 2016 and Windows 10, see MT.1024, ' + 'CVE-2021-34527, T1558.003, KB5005413, event ID 4769, port 445, 64-bit and S-1-5-32-544.' }); expect(disclosed.title).toBe('The number of DCs is too small to provide redundancy: [count] DC'); expect(disclosed.description).toBe('Last AD backup [duration] ago on [date] at [time]; built-in admin used [duration] ago. ' + '[count] accounts and [count] objects, [count] computers, [count]% of users, observed [date].'); expect(disclosed.recommendation).toBe('Disable NTLMv1 and TLS 1.0 on Windows Server 2016 and Windows 10, see MT.1024, ' + 'CVE-2021-34527, T1558.003, KB5005413, event ID 4769, port 445, 64-bit and S-1-5-32-544.'); }); it('keeps numbered remediation steps intact', () => { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceTool: 'purple-knight', title: 'Synthetic', recommendation: '1. From the impacted resources, select 3 accounts.\n2) Remove the delegation.\n 10. Verify.' }); expect(disclosed.recommendation).toBe('1. From the impacted resources, select [count] accounts.\n2) Remove the delegation.\n10. Verify.'); }); it('keeps inline ordered lists but not a lone count followed by a sentence', () => { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceTool: 'maester', title: 'Synthetic', description: 'Open the admin center. 1. Select Users. 2. Choose 3 accounts. 3. Fill the form. Found 1. Next steps follow.', recommendation: 'Enable the rules. 1) Outbound rules. 2) Network Restrictions: apply them.' }); expect(disclosed.description).toBe('Open the admin center. 1. Select Users. 2. Choose [count] accounts. 3. Fill the form. Found [count]. Next steps follow.'); expect(disclosed.recommendation).toBe('Enable the rules. 1) Outbound rules. 2) Network Restrictions: apply them.'); }); it('handles source quirks: repeated step numbers, markdown links showing their URL, code and sentence joins', () => { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceTool: 'maester', title: 'Navigate to the Microsoft 365 admin center', description: '1. Navigate to Portal [https://portal.azure.com](https://portal.azure.com). 2. Search Entra. 3. Expand Manage. ' + '3. On the Properties page continue. 4. Save. Open [blade](https://entra.microsoft.com/#view/Microsoft_AAD_IAM/' + 'RecommendationDetails.ReactView/id/x). Run (Get-Policy).keyRestrictions.isEnforced and System.DateTime ' + 'or Windows.Storage with ASP.NET. It affects any endpoint.The indicator stays. See aka.ms/mfa and ' + 'test.mysmartlogon.com; contoso.It remains.' }); expect(disclosed.title).toBe('Navigate to the Microsoft 365 admin center'); expect(disclosed.description).toBe('1. Navigate to Portal [https://portal.azure.com](https://portal.azure.com). 2. Search Entra. 3. Expand Manage. ' + '3. On the Properties page continue. 4. Save. Open [blade](https://entra.microsoft.com/#view/Microsoft_AAD_IAM/' + 'RecommendationDetails.ReactView/id/x). Run (Get-Policy).keyRestrictions.isEnforced and System.DateTime ' + 'or Windows.Storage with ASP.NET. It affects any endpoint.The indicator stays. See aka.ms/mfa and ' + '[domain]; [domain] remains.'); }); it('keeps skipped step numbers, attribute flags and CISA section numbers', () => { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceTool: 'purple-knight', title: 'Synthetic', description: '1. Create a group. 2. Open Entra. 3. Select Security. 3. Select Manage. 5. Under Admin, save. ' + 'Accounts with adminCount=1 or the adminCount attribute set to 1 are protected; 4 accounts were found. ' + 'See [CISA 4. Centralized Log Collection - MS.AAD.4.1v1].' }); expect(disclosed.description).toBe('1. Create a group. 2. Open Entra. 3. Select Security. 3. Select Manage. 5. Under Admin, save. ' + 'Accounts with adminCount=1 or the adminCount attribute set to 1 are protected; [count] accounts were found. ' + 'See [CISA 4. Centralized Log Collection - MS.AAD.4.1v1].'); }); it('keeps the space before a generic domain suffix such as .onmicrosoft.com', () => { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceTool: 'maester', sourceRuleId: 'MT.1074', title: 'No more than 100 outbound mails per day are sent using the .onmicrosoft.com domain', description: 'Spam from \'.onmicrosoft.com\' addresses , and contoso.onmicrosoft.com stays hidden .' }); expect(disclosed.title).toBe('No more than [count] outbound mails per day are sent using the .onmicrosoft.com domain'); expect(disclosed.description).toBe('Spam from \'.onmicrosoft.com\' addresses, and [domain] stays hidden.'); }); it('keeps recommended target values in recommendations only', () => { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceTool: 'pingcastle', title: 'Synthetic', description: 'The value is set to 3 on this system.', recommendation: 'RequireIntegrity and RequireMutualAuthentication should be set to 1. LDAPOwnerModify configured to 0.' }); expect(disclosed.description).toBe('The value is set to [count] on this system.'); expect(disclosed.recommendation).toBe('RequireIntegrity and RequireMutualAuthentication should be set to 1. LDAPOwnerModify configured to 0.'); }); it('keeps documented vendor values in recommendations only, never an observed count beside them', () => { const guidance = 'Set the ms-DS-MachineAccountQuota attribute on the domain NC head to 0 to disable it. ' + 'It is recommended to set it to 1. Set it to zero (0). The expected new value is 00000000010000000002000000011. ' + 'Add the flag by adding the number 1048576. Assign 1 as the Value; other values include 2 for Audit mode and 6 for Warn mode. ' + 'Use get-adobject -ldapfilter "(admincount=1)" and a 20+ length password. It can be done through 2 methods. ' + 'Note: only 10 will be listed below.'; const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceTool: 'pingcastle', title: 'Synthetic', description: guidance, recommendation: `${guidance} Currently 12 accounts are affected; set 4 of them to 0.` }); expect(disclosed.recommendation).toBe(`${guidance} Currently [count] accounts are affected; set [count] of them to [count].`); expect(disclosed.description).not.toMatch(/(? { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceTool: 'maester', title: 'Synthetic', recommendation: 'See [Microsoft Learn](https://learn.microsoft.com/en-us/entra/identity/overview) and ' + 'https://github.com/cisagov/ScubaGear/blob/main/baselines/aad.md for MS.AAD.2.3 in the Entra admin center ' + 'https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_logging?view=powershell-7 ' + 'https://www.whitehouse.gov/wp-content/uploads/2022/01/M-22-09.pdf ' + '(entra.microsoft.com). Grant Directory.ReadWrite.All and Policy.ReadWrite.AuthenticationMethod. ' + 'Check system32\\logfiles\\firewall\\pfirewall.log. ' + 'Tenant links https://contoso.sharepoint.com/sites/it, https://login.microsoftonline.com/contoso.onmicrosoft.com/oauth2, ' + 'https://portal.azure.com/#@contoso.onmicrosoft.com/resource, https://learn.microsoft.com/?tenant=contoso.de, ' + 'https://portal.azure.com/#blade/subscriptions/0f1e2d3c-4b5a-6978-8a9b-0c1d2e3f4a5b/resourceGroups/rg-contoso-prod/overview, ' + 'https://entra.microsoft.com/#view/Apps/objectId/0f1e2d3c-4b5a-6978-8a9b-0c1d2e3f4a5b/appId/x ' + 'and domains contoso.onmicrosoft.com, mail.contoso.de stay hidden.' }); expect(disclosed.recommendation).toBe('See [Microsoft Learn](https://learn.microsoft.com/en-us/entra/identity/overview) and ' + 'https://github.com/cisagov/ScubaGear/blob/main/baselines/aad.md for MS.AAD.2.3 in the Entra admin center ' + 'https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_logging?view=powershell-7 ' + 'https://www.whitehouse.gov/wp-content/uploads/2022/01/M-22-09.pdf ' + '(entra.microsoft.com). Grant Directory.ReadWrite.All and Policy.ReadWrite.AuthenticationMethod. ' + 'Check system32\\logfiles\\firewall\\pfirewall.log. ' + 'Tenant links [url], [url], [url], [url], [url], [url] ' + 'and domains [domain], [domain] stay hidden.'); }); it('keeps vendor thresholds in recommendations but not in observed titles or descriptions', () => { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceTool: 'pingcastle', title: 'Password length is at least 7 characters for 15 accounts', description: 'Observed: at least 7 characters on 15 accounts inactive for more than 55 days.', recommendation: 'A strong password is at least 8 characters, rotated every 180 days, removed when older than 90 days, ' + 'with 15 or more characters for service accounts and no more than 5 attempts; 3 accounts are affected.' }); expect(disclosed.title).toBe('Password length is at least [count] characters for [count] accounts'); expect(disclosed.description).toBe('Observed: at least [count] characters on [count] accounts inactive for more than [duration].'); expect(disclosed.recommendation).toBe('A strong password is at least 8 characters, rotated every 180 days, removed when older than 90 days, ' + 'with 15 or more characters for service accounts and no more than 5 attempts; [count] accounts are affected.'); }); it('keeps public policy and registry paths and documented authorities instead of turning them into accounts', () => { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceTool: 'cis-cat', sourceRuleId: '2.3.10.7', recommendation: 'Set the following UI path to Enabled:\n' + 'Computer Configuration\\Policies\\Windows Settings\\Security Settings\\Local Policies\\Security Options\\Network access: Remotely accessible registry paths\n' + 'The value is stored in HKLM\\SYSTEM\\CurrentControlSet\\Control\\SecurePipeServers\\Winreg\\AllowedExactPaths:Machine and ' + 'System\\CurrentControlSet\\Control\\ProductOptions.\n' + 'Grant BUILTIN\\Administrators, NT SERVICE\\WdiServiceHost and RESTRICTED SERVICES\\PrintSpoolerService.\n' + 'Do not grant CONTOSO\\jane or CONTOSO\\svc-backup.' }); expect(disclosed.recommendation).toBe('Set the following UI path to Enabled:\n' + 'Computer Configuration\\Policies\\Windows Settings\\Security Settings\\Local Policies\\Security Options\\Network access: Remotely accessible registry paths\n' + 'The value is stored in HKLM\\SYSTEM\\CurrentControlSet\\Control\\SecurePipeServers\\Winreg\\AllowedExactPaths:Machine and ' + 'System\\CurrentControlSet\\Control\\ProductOptions.\n' + 'Grant BUILTIN\\Administrators, NT SERVICE\\WdiServiceHost and RESTRICTED SERVICES\\PrintSpoolerService.\n' + 'Do not grant [account] or [account].'); }); it('keeps Group Policy node paths, hard-wrapped registry paths, rule references and documented address ranges', () => { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceTool: 'cis-cat', sourceRuleId: '2.3.10.9', description: 'Use the built-in security template of Group Policy (e.g. Windows Settings\\Security Settings). ' + 'It depends on Network security: LAN Manager Authentication Level (Rule 2.3.11.7).', recommendation: 'System\\CurrentControlSet\\Control\\Terminal Server System\\CurrentControlSet\\Control\\Terminal\n' + 'Server\\UserConfig System\\CurrentControlSet\\Control\\ContentIndex.\nCONTOSO\\jane must not be listed.' }); expect(disclosed.description).toBe('Use the built-in security template of Group Policy (e.g. Windows Settings\\Security Settings). ' + 'It depends on Network security: LAN Manager Authentication Level (Rule 2.3.11.7).'); expect(disclosed.recommendation).toBe('System\\CurrentControlSet\\Control\\Terminal Server System\\CurrentControlSet\\Control\\Terminal\n' + 'Server\\UserConfig System\\CurrentControlSet\\Control\\ContentIndex.\n[account] must not be listed.'); const firewall = discloseResultDbProjection({ ...unsafeScanner(), sourceTool: 'pingcastle', sourceRuleId: 'S-FirewallScript', title: 'Synthetic', recommendation: 'Allow local network access: 10.0.0.0 to 10.255.255.255, 192.168.0.0 to 192.168.255.255; ' + 'block 0.0.0.0 to 9.255.255.255 11.0.0.0 to 126.255.255.255 128.0.0.1 to 172.15.255.255 172.32.0.0 to 192.167.255.255 ' + '192.169.0.0 to 255.255.255.255; host 10.20.30.40 and subnet 10.20.30.0 to 10.20.30.255 observed.' }); expect(firewall.recommendation).toBe('Allow local network access: 10.0.0.0 to 10.255.255.255, 192.168.0.0 to 192.168.255.255; ' + 'block 0.0.0.0 to 9.255.255.255 11.0.0.0 to 126.255.255.255 128.0.0.1 to 172.15.255.255 172.32.0.0 to 192.167.255.255 ' + '192.169.0.0 to 255.255.255.255; ' + 'host [ip] and subnet [ip] to [ip] observed.'); }); it('keeps multi-part CIS numbers in titles and redacts only standalone IPv4 addresses', () => { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceTool: 'cis-cat', sourceRuleId: '18.9.20.1.1', title: "18.9.20.1.1 (L2) Ensure 'Turn off downloading of print drivers over HTTP' is set to 'Enabled'", description: 'Rule 18.9.20.1 applies; the server 10.20.30.40 and 999.1.1.1 were observed.' }); expect(disclosed.title).toBe("18.9.20.1.1 (L2) Ensure 'Turn off downloading of print drivers over HTTP' is set to 'Enabled'"); expect(disclosed.description).toBe('Rule 18.9.20.1 applies; the server [ip] and 999.1.1.1 were observed.'); }); it('replaces compact durations such as 24h or 30d', () => { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceTool: 'pingcastle', title: 'Synthetic', description: 'Rotated 30d ago, checked every 24h, 5min, 2yrs.' }); expect(disclosed.description).toBe('Rotated [duration] ago, checked every [duration], [duration], [duration].'); }); it('keeps registry-path GPO titles readable instead of treating them as DOMAIN\\account pairs', () => { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceKind: 'policy-evaluation', sourceTool: 'GPOs', title: 'Software\\Policies\\Microsoft\\Windows NT\\Printers · RestrictDriverInstallationToAdministrators' }); expect(disclosed.title).toBe('Software\\Policies\\Microsoft\\Windows NT\\Printers · RestrictDriverInstallationToAdministrators'); }); it('keeps documented Microsoft principals in the Soll value readable', () => { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceKind: 'policy-evaluation', sourceTool: 'GPOs', title: 'Privilege Rights · SeServiceLogonRight', policyBaselineValue: { kind: 'principal-set', principals: ['*S-1-5-113', '*S-1-5-83-0', 'S-1-5-9', 'NT SERVICE\\WdiServiceHost', 'NT VIRTUAL MACHINE\\Virtual Machines', 'Window Manager\\Window Manager Group', 'CONTOSO\\svc-backup', 'S-1-5-21-1-2-3-1105'] } }); expect(disclosed.policyBaselineValue).toEqual({ kind: 'principal-set', principals: ['*S-1-5-113', '*S-1-5-83-0', 'S-1-5-9', 'NT SERVICE\\WdiServiceHost', 'NT VIRTUAL MACHINE\\Virtual Machines', 'Window Manager\\Window Manager Group', '[DOMAIN_PRINCIPAL_1]', 'S-1-5-21-[DOMAIN]-1105'] }); }); it('keeps public benchmark values in CIS-CAT prose', () => { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceTool: 'cis-cat', sourceRuleId: '1.1.4', title: "(L1) Ensure 'Minimum password length' is set to '14 or more character(s)'", recommendation: "Set 'Account lockout duration' to 15 or more minute(s)." }); expect(disclosed.title).toBe("(L1) Ensure 'Minimum password length' is set to '14 or more character(s)'"); expect(disclosed.recommendation).toBe("Set 'Account lockout duration' to 15 or more minute(s)."); }); it('never discloses an unprotected number from non-benchmark scanner prose', () => { const fingerprints = ['7 DC', '15 accounts', '55 days', '0 day(s) ago', '2026-08-04', '13:37', '1.015', '3,250']; for (const tool of ['pingcastle', 'purple-knight', 'maester']) { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceTool: tool, title: `Synthetic ${fingerprints.join(', ')}`, description: fingerprints.join(' / '), recommendation: `Review ${fingerprints.join(' and ')}` }); const prose = [disclosed.title, disclosed.description, disclosed.recommendation].join('\n'); expect(prose.replace(/\[(?:count|duration|date|time)\]/g, '')).not.toMatch(/\d/); } }); it('discloses only the baseline family instead of the compared release', () => { for (const [label, family] of [ ['Microsoft Security Baseline Windows Server 2025 (2602)', 'Microsoft Security Baseline - Windows Server'], ['Windows 11 25H2 Security Baseline', 'Microsoft Security Baseline - Windows Client'], ['Microsoft 365 Apps for enterprise 2512', 'Microsoft Security Baseline - Microsoft 365 Apps'], ['Private customer template', 'Baseline recommendation'] ]) { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceKind: 'policy-evaluation', sourceTool: 'GPOs', title: 'Synthetic setting', policyBaselineLabel: label }); expect(disclosed.policyBaselineLabel).toBe(family); } }); it('collapses identical baseline expectations so the compared release count is not disclosed', () => { const variant = (id: string) => ({ settingId: id, baselineValues: [], comparator: { version: 1 as const, comparator: { kind: 'equality' as const }, relation: 'different' as const, actual: { kind: 'text' as const, value: '0' }, baseline: { kind: 'text' as const, value: '1' }, diagnosticCodes: [] } }); const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceKind: 'policy-evaluation', sourceTool: 'GPOs', title: 'Synthetic setting', policySourceCount: 4, policyBaselineVariantAssessment: { version: 1, scope: 'backup-configuration', decision: 'all-fail', actualValues: [], variants: [variant('server-2019'), variant('server-2022'), variant('server-2025')], diagnosticCodes: [] } }); expect(disclosed.policyBaselineVariantAssessment?.variants).toHaveLength(1); expect(disclosed).not.toHaveProperty('policySourceCount'); expect(JSON.stringify(disclosed)).not.toMatch(/server-20\d\d/); }); it('retains Purple Knight native rule identity and remediation text', () => { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceTool: 'purple-knight', sourceRuleId: 'SI000055', title: 'Privileged accounts have weak delegation settings', description: 'Review delegation on PRIVATE\\admin-user and private-company.example.', recommendation: '
Remove unconstrained delegation from the affected account.
', affectedAsset: 'PRIVATE\\admin-user' }); expect(disclosed).toMatchObject({ sourceTool: 'purple-knight', sourceRuleId: 'SI000055', title: 'Privileged accounts have weak delegation settings', description: 'Review delegation on [account] and [domain].', recommendation: 'Remove unconstrained delegation from the affected account.' }); }); it('publishes Purple Knight for Entra ID as its own Result DB source scope', () => { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceTool: 'purple-knight', sourceToolScope: 'entra-id', sourceRuleId: 'SI000055', title: 'Synthetic Entra control', description: 'Synthetic Entra description.' }); expect(disclosed).toMatchObject({ sourceTool: 'purple-knight-entra', sourceToolScope: 'entra-id', sourceRuleId: 'SI000055', title: 'Synthetic Entra control', categories: [{ namespace: 'source-tool', categoryCode: 'purple-knight-entra', categoryLabel: 'Purple Knight for Entra ID' }] }); }); it('shows a publicly documented PingCastle ID in its native spelling and rejects an unknown ID', () => { const item = { ...unsafeScanner(), sourceTool: 'pingcastle', sourceRuleId: 'S-oldNtlm', title: 'The LAN Manager Authentication Level allows the use of NTLMv1 or LM.' }; expect(discloseResultDbProjection(item)).toMatchObject({ sourceRuleId: 'S-oldNtlm', title: item.title }); expect(discloseResultDbProjection({ ...item, sourceRuleId: 'S-PrivateCustomerRule' }).sourceRuleId) .toBeUndefined(); }); it('releases documented Maester suite IDs but keeps unknown and instance-specific IDs private', () => { for (const sourceRuleId of ['CIS.M365.1.1.1', 'CISA.MS.AAD.2.3', 'EIDSCA.AF01', 'ORCA.100', 'MT.1025']) { expect(discloseResultDbProjection({ ...unsafeScanner(), sourceRuleId }).sourceRuleId) .toBe(sourceRuleId); } for (const sourceRuleId of ['CT001', 'MT.INV.001', 'MT.1024.private-customer-key', 'MT.1024.applicationCredentialExpiry.private-tenant', 'MT.1033.0', 'CIS.M365.99.99.99']) { expect(discloseResultDbProjection({ ...unsafeScanner(), sourceRuleId }).sourceRuleId) .toBeUndefined(); } }); it('keeps a CIS-CAT control number and public policy path while hiding an actual address and account', () => { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceTool: 'cis-cat', sourceRuleId: '18.10.36.1', title: "18.10.36.1 Ensure 'Turn off location' is set to 'Enabled'", recommendation: 'Computer Configuration\\Administrative Templates\\System\\Location and Sensors\\Turn off location; CONTOSO\\jane\n' + 'Use the public policy file Windows.adml.\n' + 'Review CONTOSO\\jane at 10.20.30.40 and jane@contoso.example.' }); expect(disclosed.sourceRuleId).toBe('18.10.36.1'); expect(disclosed.title).toContain('18.10.36.1'); expect(disclosed.title).not.toContain('[ip]'); expect(disclosed.recommendation).toContain('Computer Configuration\\Administrative Templates\\System\\Location and Sensors\\Turn off location'); expect(disclosed.recommendation).toContain('Turn off location; [account]'); expect(disclosed.recommendation).toContain('Windows.adml'); expect(disclosed.recommendation).toContain('[account] at [ip] and [email]'); expect(disclosed.recommendation).not.toMatch(/CONTOSO|jane|10\.20\.30\.40/); }); it('keeps only the GPO Soll value and withholds every customer value and principal', () => { const systemSid = 'S-1-5-82-123456789-234567890-345678901-456789012-567890123'; const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceKind: 'policy-evaluation', sourceTool: 'GPOs', title: 'System Access · LockoutBadCount', policyActualValue: { kind: 'text', value: '5', principals: [systemSid, 'S-1-5-32-544'] }, policyBaselineValue: { kind: 'text', value: '10' }, policyBaselineVariantAssessment: { version: 1, scope: 'backup-configuration', decision: 'all-fail', actualValues: [{ kind: 'principal-set', principals: [systemSid] }], variants: [{ settingId: 'private-setting', baselineValues: [], comparator: { version: 1, comparator: { kind: 'set', mode: 'exact' }, relation: 'different', actual: { kind: 'string-set', values: [systemSid] }, baseline: { kind: 'string-set', values: ['S-1-5-32-544'] }, diagnosticCodes: [] }}], diagnosticCodes: [] } }); expect(disclosed).not.toHaveProperty('policyActualValue'); expect(disclosed.policyBaselineValue).toMatchObject({ kind: 'text', value: '10' }); expect(disclosed.policyBaselineVariantAssessment).toMatchObject({ decision: 'all-fail', actualValues: [], variants: [{ comparator: { relation: 'different', baseline: { kind: 'string-set', values: ['S-1-5-32-544'] } } }] }); expect(disclosed.policyBaselineVariantAssessment?.variants[0].comparator).not.toHaveProperty('actual'); const serialized = JSON.stringify(disclosed); expect(serialized).not.toContain(systemSid); expect(serialized).not.toContain('PRINCIPAL'); expect(serialized).not.toContain('"5"'); }); it('keeps the technical GPO setting, Soll value and relation without customer values or source identity', () => { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceKind: 'policy-evaluation', sourceTool: 'GPOs', sourceRuleId: 'registry:machine:software\\policies\\microsoft\\windows nt\\printers\\pointandprint:restrictdriverinstallationtoadministrators', title: 'Restrict driver installation to administrators', description: 'Private Company GmbH configures this setting for dummy.local.', recommendation: 'Set RestrictDriverInstallationToAdministrators to 1.', policyDeliveryMechanism: 'registry-policy', policyClassification: 'misconfigured', policyActualValue: { kind: 'registry-policy', operation: 'set-value', key: 'Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint', valueName: 'RestrictDriverInstallationToAdministrators', typeCode: 4, data: { kind: 'uint32', value: 0 }, principals: ['S-1-5-18', 'S-1-5-32-544', 'BUILTIN\\Administrators', 'DUMMY\\Domain Admins', 'DUMMY\\jane.doe', 'S-1-5-21-123456789-234567890-345678901-512'], note: 'Configured by {A1B2C3D4-1111-2222-3333-444455556666}', gpoName: 'DUMMY – Domain Controllers Security', gpoId: '{A1B2C3D4-1111-2222-3333-444455556666}', sourcePath: '\\\\fileserver\\backups\\dummy.local\\gpo.zip' }, policyBaselineValue: { kind: 'registry-policy', operation: 'set-value', key: 'Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint', valueName: 'RestrictDriverInstallationToAdministrators', typeCode: 4, data: { kind: 'uint32', value: 1 } }, policyIdentityKey: 'private-company-policy', policyGpoId: '{A1B2C3D4-1111-2222-3333-444455556666}', policyNativeBackupId: '{99999999-1111-2222-3333-444455556666}', policyBaselineId: 'private-baseline-id', policyBaselineLabel: 'Private Company Microsoft Security Baseline Windows Server 2022 v1', policyDiagnosticCodes: ['policy-safe-code', 'Private Company'], policyComparisonRelation: 'different', policyBaselineVariantAssessment: { version: 1, scope: 'backup-configuration', decision: 'all-fail', actualValues: [{ kind: 'principal-set', principals: ['DUMMY\\named.user'] }], variants: [{ settingId: 'private-company-baseline-setting', baselineValues: [{ kind: 'principal-set', principals: ['BUILTIN\\Administrators'] }], comparator: { version: 1, comparator: { kind: 'set', mode: 'exact' }, relation: 'different', actual: { kind: 'string-set', values: ['DUMMY\\named.user'] }, baseline: { kind: 'string-set', values: ['BUILTIN\\Administrators'] }, diagnosticCodes: [] } }], diagnosticCodes: [] } }); expect(disclosed).toMatchObject({ title: 'Restrict driver installation to administrators', sourceTool: 'GPOs', sourceRuleId: 'registry:machine:software\\policies\\microsoft\\windows nt\\printers\\pointandprint:restrictdriverinstallationtoadministrators', policyBaselineValue: { data: { kind: 'uint32', value: 1 } }, policyBaselineLabel: 'Microsoft Security Baseline - Windows Server', policyComparisonRelation: 'different', recommendation: 'Set RestrictDriverInstallationToAdministrators to 1.', policyDiagnosticCodes: ['policy-safe-code'], policyBaselineVariantAssessment: { actualValues: [], variants: [{ settingId: 'baseline-expectation-1', comparator: { relation: 'different', baseline: { kind: 'string-set', values: ['BUILTIN\\Administrators'] } }}] } }); expect(disclosed).not.toHaveProperty('policyActualValue'); expect(disclosed.policyIdentityKey).toBeUndefined(); expect(disclosed.policyBaselineId).toBeUndefined(); const serialized = JSON.stringify(disclosed); for (const privateValue of ['Private Company', 'dummy.local', 'DUMMY', 'Domain Admins', 'PRINCIPAL', '2022', 'A1B2C3D4', '99999999', 'fileserver', 'jane.doe', 'named.user', '123456789', '234567890', '345678901']) { expect(serialized).not.toContain(privateValue); } expect(serialized).toContain('Backup comparison; effective endpoint state not verified'); }); it('removes generated GPO identity hashes instead of presenting them as rule IDs', () => { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceKind: 'policy-evaluation', sourceTool: 'GPOs', sourceRuleId: 'gpo-policy-identity-deadbeef', title: 'Synthetic setting', policyBaselineLabel: 'Microsoft Security Baseline', policyActualValue: 0, policyBaselineValue: 1 }); expect(disclosed.sourceRuleId).toBeUndefined(); expect(disclosed.policyIdentityKey).toBeUndefined(); }); it('replaces customer scope handles after query filtering', () => { expect(removeResultDbScopeIdentifiers(discloseResultDbProjection(unsafeScanner()))).toMatchObject({ projectId: 'scoped-project', assessmentId: 'scoped-assessment', collectionRunId: 'scoped-collection', scanRunId: 'scoped-scan' }); }); it('keeps the complete seven-level CIS number so sibling recommendations do not collide', () => { const first = discloseResultDbProjection({ ...unsafeScanner(), sourceTool: 'cis-cat', sourceRuleId: '18.10.42.11.1.1.1' }); const second = discloseResultDbProjection({ ...unsafeScanner(), sourceTool: 'cis-cat', sourceRuleId: 'xccdf_org.cisecurity.benchmarks_rule_18.10.42.11.1.1.2_L1_Ensure' }); expect(first.sourceRuleId).toBe('18.10.42.11.1.1.1'); expect(second.sourceRuleId).toBe('18.10.42.11.1.1.2'); expect(discloseResultDbProjection({ ...unsafeScanner(), sourceTool: 'cis-cat', sourceRuleId: '1.2.3.4.5.6.7.8.9.10' }).sourceRuleId) .toBeUndefined(); }); it('does not mistake the ordinary word "limited" for an organization but still redacts legal names', () => { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), title: 'Limited number of Global Admins are assigned.', description: 'External sharing limited to existing guests.', recommendation: 'Contact Contoso Ltd or Fabrikam GmbH.' }); expect(disclosed.title).toBe('Limited number of Global Admins are assigned.'); expect(disclosed.description).toBe('External sharing limited to existing guests.'); expect(disclosed.recommendation).not.toMatch(/Contoso|Fabrikam/); expect(disclosed.recommendation).toContain('[organization]'); }); it('keeps public versions, releases and step, option and page references in scanner prose', () => { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceTool: 'pingcastle', sourceRuleId: 'A-DCLdapsProtocolAdvanced', title: 'At least one DC is using Tls1.0 or Tls1.1.', description: 'NTLMv2 works starting Windows 95. See Microsoft Graph v1.0. If using a pre 2012-R2 schema, ' + 'divide the value from step 2 by the value from step 3. Option 1 is described on Page 16. 4 DCs were observed.' }); expect(disclosed.title).toBe('At least one DC is using Tls1.0 or Tls1.1.'); expect(disclosed.description).toBe('NTLMv2 works starting Windows 95. See Microsoft Graph v1.0. If using a pre 2012-R2 schema, ' + 'divide the value from step 2 by the value from step 3. Option 1 is described on Page 16. [count] DCs were observed.'); }); it('keeps documented Microsoft GUIDs but redacts every other GUID', () => { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), description: 'Add Azure DevOps (App ID: 499b84ac-1321-427f-aa17-267ca6975798) and block with ASR rule ' + 'd3e037e1-3eb8-44c8-a917-57927947596d. Tenant 11111111-2222-4333-8444-555555555555 is affected.' }); expect(disclosed.description).toBe('Add Azure DevOps (App ID: 499b84ac-1321-427f-aa17-267ca6975798) and block with ASR rule ' + 'd3e037e1-3eb8-44c8-a917-57927947596d. Tenant [id] is affected.'); }); it('withholds per-finding scanner scores', () => { const disclosed = discloseResultDbProjection({ ...unsafeScanner(), sourceTool: 'purple-knight', sourceRuleId: 'SI000011', score: 73 }); expect(disclosed).not.toHaveProperty('score'); }); it('lists withheld fields in the trace without revealing client review state', () => { const reviewed = discloseResultDbProjectionWithTrace({ ...unsafeScanner(), reviewDisposition: 'accepted', reviewPriority: 'high' }); const unreviewed = discloseResultDbProjectionWithTrace({ ...unsafeScanner(), reviewStatus: undefined, reviewOwner: undefined, reviewDueDate: undefined }); expect(reviewed.disclosureTrace?.withheldFields).toEqual(unreviewed.disclosureTrace?.withheldFields); expect(JSON.stringify(reviewed.disclosureTrace)).not.toMatch(/review/i); expect(reviewed.disclosureTrace?.withheldFields).toContain('affectedAsset'); }); });