import { createHash } from 'node:crypto'; import { describe, expect, it } from 'vitest'; import { parseGroupPolicyPreferenceXml, reduceGroupPolicyPreferences } from '../../server/gpo/group-policy-preference-parser'; import type { VerifiedZipFile } from '../../server/gpo/zip-inventory'; import { ImportError } from '../../src/shared/errors'; function verifiedFile(content: string | Uint8Array, name = 'Registry.xml'): VerifiedZipFile { const bytes = typeof content === 'string' ? new TextEncoder().encode(content) : content; return { entry: { index: 1, relativePath: `synthetic/DomainSysvol/GPO/Machine/Preferences/Registry/${name}`, kind: 'file', compressionMethod: 8, compressedBytes: bytes.byteLength, uncompressedBytes: bytes.byteLength, crc32: '00000000', sha256: createHash('sha256').update(bytes).digest('hex') }, bytes }; } function context() { return { backupInstanceId: 'backup-synthetic', sourceArtifactId: 'source-synthetic', artifactId: 'entry-synthetic' }; } const rootClsid = '{A3CCFC41-DFDB-43a5-8D26-0FE8B954DA51}'; const itemClsid = '{9CD4B2F4-923D-47f5-A062-E897DD1DAD50}'; describe('Group Policy Preferences parser and Registry reducer', () => { it('preserves Registry action, order, common options and opaque item-level targeting', () => { const document = parseGroupPolicyPreferenceXml(verifiedFile([ ``, ` `, ' ', ' ', ' ', ` `, ' ', ' ', '' ].join('\r\n')), 'computer'); expect(document).toMatchObject({ status: 'complete', preferenceType: 'registry', secretIndicatorStatus: 'not-detected' }); const observations = reduceGroupPolicyPreferences(document, context()); expect(observations).toHaveLength(2); expect(observations[0]).toMatchObject({ deliveryMechanism: 'group-policy-preference', state: 'instruction', identity: { namespace: 'group-policy-preference', section: 'registry' }, preference: { cse: 'registry', operation: 'replace', sequence: 0, disabled: false, removePolicy: true, applyOnce: false, targeting: { status: 'present', filterTypes: ['filtergroup'], evaluability: 'unknown' } }, values: [{ kind: 'registry-preference', hive: 'HKEY_LOCAL_MACHINE', key: 'Software\\Synthetic', target: 'value', valueName: 'Mode', valueType: 'REG_SZ', rawData: 'Strict' }], sourceLines: [2], effectiveState: 'unknown', diagnosticCodes: ['gpo-preference-targeting-unevaluated'] }); expect(observations[1]).toMatchObject({ preference: { operation: 'delete', sequence: 1 }, sourceLines: [6] }); }); it('recognizes but never returns cpassword values', () => { const secret = 'synthetic-secret-must-not-leave-parser'; const document = parseGroupPolicyPreferenceXml(verifiedFile( `` ), 'user'); expect(document).toMatchObject({ status: 'complete', secretIndicatorStatus: 'present' }); expect(document.diagnosticCodes).toContain('gpo-secret-indicator-present'); expect(JSON.stringify(document)).not.toContain(secret); expect(JSON.stringify(reduceGroupPolicyPreferences(document, context()))).not.toContain(secret); }); it('keeps unsupported Preference CSEs partial without producing policy observations', () => { const document = parseGroupPolicyPreferenceXml(verifiedFile( '', 'Groups.xml' ), 'computer'); expect(document).toMatchObject({ preferenceType: 'unsupported', status: 'partial', secretIndicatorStatus: 'present', instructions: [], diagnosticCodes: ['gpo-preference-type-unsupported', 'gpo-secret-indicator-present'] }); expect(reduceGroupPolicyPreferences(document, context())).toEqual([]); }); it('fails closed for malformed supported actions, targets, common flags and targeting', () => { const document = parseGroupPolicyPreferenceXml(verifiedFile( `` ), 'computer'); expect(document.status).toBe('invalid'); expect(document.instructions).toEqual([]); expect(document.diagnosticCodes).toEqual(expect.arrayContaining([ 'gpo-preference-remove-policy-invalid', 'gpo-preference-targeting-invalid' ])); }); it('keeps unsupported Registry subproperties partial instead of inventing bitfield semantics', () => { const document = parseGroupPolicyPreferenceXml(verifiedFile( '' ), 'computer'); expect(document).toMatchObject({ status: 'partial', instructions: [], diagnosticCodes: ['gpo-preference-registry-item-unsupported'] }); expect(reduceGroupPolicyPreferences(document, context())).toEqual([]); }); it('rejects active XML constructs before parsing', () => { expect(() => parseGroupPolicyPreferenceXml(verifiedFile( `]>&x;` ), 'computer')).toThrow(ImportError); }); });