61 lines
4.7 KiB
TypeScript
61 lines
4.7 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { parseGpoBackupMetadataXml } from '../../server/gpo/gpo-metadata-parser';
|
|
import type { VerifiedZipFile } from '../../server/gpo/zip-inventory';
|
|
const guid = '11111111-1111-4111-8111-111111111111';
|
|
function parse(xml: string) {
|
|
return parseGpoBackupMetadataXml({ bytes: Buffer.from(xml), entry: {
|
|
relativePath: 'synthetic/Backup.xml', sha256: 'a'.repeat(64)
|
|
} } as unknown as VerifiedZipFile);
|
|
}
|
|
describe('GPMC metadata field paths', () => {
|
|
it('reads names only from bounded template fields when explicitly requested', () => {
|
|
const file = { bytes: Buffer.from('<GPO><Name>MSFT Windows Server 2016 - Domain Controller</Name><Other><Name>Wrong</Name></Other></GPO>'),
|
|
entry: { relativePath: 'Synthetic/GPReport.xml', sha256: 'a'.repeat(64) } } as unknown as VerifiedZipFile;
|
|
expect(parseGpoBackupMetadataXml(file).templateNames).toBeUndefined();
|
|
expect(parseGpoBackupMetadataXml(file, undefined, true).templateNames).toEqual(['MSFT Windows Server 2016 - Domain Controller']);
|
|
const withIdentity = { ...file, bytes: Buffer.from(`<GPO><Name>Arbitrary display name</Name><Identifier><Identifier>{${guid}}</Identifier></Identifier></GPO>`) };
|
|
expect(parseGpoBackupMetadataXml(withIdentity, undefined, true).gpoId).toBe(guid);
|
|
expect(parseGpoBackupMetadataXml(file, undefined, true).gpoId).toBeUndefined();
|
|
});
|
|
it('reads the GPMC core identity without interpreting unrelated IDs as a GPO', () => {
|
|
const result = parse(`<GroupPolicyBackupScheme xmlns="http://www.microsoft.com/GroupPolicy/GPOOperations"><GroupPolicyObject>
|
|
<SecurityGroups><Group><ID>22222222-2222-4222-8222-222222222222</ID><Domain>unrelated.example</Domain></Group></SecurityGroups>
|
|
<GroupPolicyCoreSettings><ID>{${guid}}</ID><Domain>synthetic.example</Domain></GroupPolicyCoreSettings>
|
|
</GroupPolicyObject></GroupPolicyBackupScheme>`);
|
|
expect(result).toMatchObject({ gpoId: guid, domainKey: 'dns:synthetic.example', status: 'partial' });
|
|
expect(result.backupId).toBeUndefined();
|
|
expect(result.backupCreatedAt).toBeUndefined();
|
|
});
|
|
it('reads a nested GPReport Identifier and preserves missing backup-specific evidence', () => {
|
|
const result = parse(`<GPO xmlns="http://www.microsoft.com/GroupPolicy/Settings"><Identifier><Identifier>{${guid}}</Identifier>
|
|
<Domain>synthetic.example</Domain></Identifier></GPO>`);
|
|
expect(result.gpoId).toBe(guid);
|
|
expect(result.domainKey).toBe('dns:synthetic.example');
|
|
expect(result.diagnosticCodes).not.toContain('gpo-metadata-field-shape-invalid');
|
|
expect(result.diagnosticCodes).toContain('gpo-metadata-backup-id-missing');
|
|
});
|
|
it('retains GPReport link counts only, never the linked SOM path', () => {
|
|
const report = (links: string) => parseGpoBackupMetadataXml({ bytes: Buffer.from(`<GPO xmlns="http://www.microsoft.com/GroupPolicy/Settings">
|
|
<Identifier><Identifier>{${guid}}</Identifier><Domain>synthetic.example</Domain></Identifier><Name>Synthetic GPO</Name>${links}</GPO>`),
|
|
entry: { relativePath: 'synthetic/gpreport.xml', sha256: 'a'.repeat(64) } } as unknown as VerifiedZipFile);
|
|
const linked = report('<LinksTo><SOMName>Synthetic OU</SOMName><SOMPath>synthetic.example/Synthetic OU</SOMPath><Enabled>true</Enabled></LinksTo>'
|
|
+ '<LinksTo><SOMPath>synthetic.example</SOMPath><Enabled>false</Enabled></LinksTo>');
|
|
expect(linked.parser.version).toBe('1.4.0');
|
|
expect(linked.links).toEqual({ status: 'linked', linkCount: 2, enabledLinkCount: 1 });
|
|
expect(JSON.stringify(linked)).not.toContain('Synthetic OU');
|
|
expect(report('<LinksTo><SOMPath>synthetic.example</SOMPath><Enabled>false</Enabled></LinksTo>').links)
|
|
.toEqual({ status: 'not-linked', linkCount: 1, enabledLinkCount: 0 });
|
|
expect(report('').links).toEqual({ status: 'not-linked', linkCount: 0, enabledLinkCount: 0 });
|
|
// Backup.xml carries no link facts; the baseline template analyzer keeps its own 1.3.0 contract.
|
|
expect(parse(`<GroupPolicyBackupScheme><GroupPolicyObject><GroupPolicyCoreSettings><ID>{${guid}}</ID></GroupPolicyCoreSettings></GroupPolicyObject></GroupPolicyBackupScheme>`).links).toBeUndefined();
|
|
const template = parseGpoBackupMetadataXml({ bytes: Buffer.from('<GPO><Name>Synthetic</Name></GPO>'),
|
|
entry: { relativePath: 'synthetic/gpreport.xml', sha256: 'a'.repeat(64) } } as unknown as VerifiedZipFile, undefined, true);
|
|
expect(template.parser.version).toBe('1.3.0');
|
|
expect(template.links).toBeUndefined();
|
|
});
|
|
it('keeps depth limits and rejects external entities', () => {
|
|
expect(() => parse('<root>'.repeat(129) + '</root>'.repeat(129))).toThrow();
|
|
expect(() => parse('<!DOCTYPE GPO [<!ENTITY e SYSTEM "file:///synthetic">]><GPO>&e;</GPO>')).toThrow();
|
|
});
|
|
});
|