127 lines
7.2 KiB
TypeScript
127 lines
7.2 KiB
TypeScript
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 { gpoPolicyObservationValidationErrors } from '../../src/domain/gpo-policy';
|
|
|
|
const rootClsid = '{8FDDCC1A-0C3C-43cd-A6B4-71A6DF20DA8C}';
|
|
const itemClsid = '{935D1B74-9CB8-4e3c-9914-7DD559B7A417}';
|
|
|
|
function verifiedFile(content: string): VerifiedZipFile {
|
|
const bytes = new TextEncoder().encode(content);
|
|
return {
|
|
entry: {
|
|
index: 1,
|
|
relativePath: 'synthetic/DomainSysvol/GPO/User/Preferences/Drives/Drives.xml',
|
|
kind: 'file',
|
|
compressionMethod: 8,
|
|
compressedBytes: bytes.byteLength,
|
|
uncompressedBytes: bytes.byteLength,
|
|
crc32: '00000000',
|
|
sha256: createHash('sha256').update(bytes).digest('hex')
|
|
},
|
|
bytes
|
|
};
|
|
}
|
|
|
|
const context = { backupInstanceId: 'backup-synthetic', sourceArtifactId: 'source-synthetic', artifactId: 'entry-synthetic' };
|
|
|
|
describe('Drive Maps preference CSE', () => {
|
|
it('preserves mapping, credential-presence, visibility and targeting without retaining cpassword', () => {
|
|
const secret = 'synthetic-drive-secret-never-return';
|
|
const document = parseGroupPolicyPreferenceXml(verifiedFile([
|
|
`<?xml version="1.0"?><Drives clsid="${rootClsid}">`,
|
|
` <Drive clsid="${itemClsid}" uid="{11111111-1111-4111-8111-111111111111}" removePolicy="1" applyOnce="1">`,
|
|
` <Properties action="R" path="\\\\synthetic-server\\share" label="Synthetic Share" persistent="1" userName="SYNTHETIC\\DriveUser" cpassword="${secret}" useLetter="1" letter="S" thisDrive="SHOW" allDrives="HIDE"/>`,
|
|
' <Filters><FilterGroup bool="AND" not="0" name="SYNTHETIC\\Operators"/></Filters>',
|
|
' </Drive>',
|
|
'</Drives>'
|
|
].join('\r\n')), 'user');
|
|
|
|
expect(document).toMatchObject({ preferenceType: 'drive-maps', status: 'complete', secretIndicatorStatus: 'present' });
|
|
expect(JSON.stringify(document)).not.toContain(secret);
|
|
const observations = reduceGroupPolicyPreferences(document, context);
|
|
expect(JSON.stringify(observations)).not.toContain(secret);
|
|
expect(observations).toEqual([expect.objectContaining({
|
|
scope: 'user',
|
|
identity: { status: 'unresolved', namespace: 'group-policy-preference', section: 'drive map', key: 'S:exact' },
|
|
state: 'instruction',
|
|
values: [{
|
|
kind: 'drive-map-preference', path: '\\\\synthetic-server\\share', label: 'Synthetic Share',
|
|
persistent: true, userName: 'SYNTHETIC\\DriveUser', passwordAttributePresent: true,
|
|
letter: 'S', useLetter: true, selectionMode: 'exact', thisDrive: 'show', allDrives: 'hide'
|
|
}],
|
|
preference: expect.objectContaining({
|
|
cse: 'drive-maps', operation: 'replace', sequence: 0, removePolicy: true, applyOnce: true,
|
|
targeting: expect.objectContaining({ status: 'present', evaluability: 'unknown' })
|
|
}),
|
|
sourceLines: [2], effectiveState: 'unknown',
|
|
diagnosticCodes: ['gpo-preference-targeting-unevaluated', 'gpo-secret-indicator-present']
|
|
})]);
|
|
});
|
|
|
|
it('keeps the action-dependent meaning of useLetter and source order', () => {
|
|
const item = (uid: string, properties: string) =>
|
|
`<Drive clsid="${itemClsid}" uid="{${uid}}"><Properties ${properties}/></Drive>`;
|
|
const document = parseGroupPolicyPreferenceXml(verifiedFile([
|
|
`<Drives clsid="${rootClsid}">`,
|
|
item('22222222-2222-4222-8222-222222222222', 'action="C" path="\\\\server\\create" useLetter="0" letter="P"'),
|
|
item('33333333-3333-4333-8333-333333333333', 'action="D" useLetter="0" letter="Q"'),
|
|
item('44444444-4444-4444-8444-444444444444', 'action="R" path="\\\\server\\replace" useLetter="0" letter="R"'),
|
|
item('55555555-5555-4555-8555-555555555555', 'action="U" path="" useLetter="1" letter="S"'),
|
|
'</Drives>'
|
|
].join('\n')), 'user');
|
|
|
|
expect(document.status).toBe('complete');
|
|
if (document.preferenceType !== 'drive-maps') throw new Error('Expected Drive Maps document.');
|
|
expect(document.instructions.map(instruction => ({
|
|
operation: instruction.operation,
|
|
sequence: instruction.sequence,
|
|
selectionMode: instruction.value.selectionMode,
|
|
letter: instruction.value.letter
|
|
}))).toEqual([
|
|
{ operation: 'create', sequence: 0, selectionMode: 'first-available', letter: 'P' },
|
|
{ operation: 'delete', sequence: 1, selectionMode: 'delete-range', letter: 'Q' },
|
|
{ operation: 'replace', sequence: 2, selectionMode: 'first-mapped-in-range', letter: 'R' },
|
|
{ operation: 'update', sequence: 3, selectionMode: 'exact', letter: 'S' }
|
|
]);
|
|
});
|
|
|
|
it('fails closed for malformed supported letter, UNC, visibility and boolean fields', () => {
|
|
const document = parseGroupPolicyPreferenceXml(verifiedFile(
|
|
`<Drives clsid="${rootClsid}"><Drive clsid="${itemClsid}" uid="{66666666-6666-4666-8666-666666666666}"><Properties action="C" path="C:\\local" persistent="2" useLetter="1" letter="AA" thisDrive="VISIBLE"/></Drive></Drives>`
|
|
), 'user');
|
|
expect(document).toMatchObject({ status: 'invalid', instructions: [] });
|
|
expect(document.diagnosticCodes).toEqual(expect.arrayContaining([
|
|
'gpo-preference-boolean-invalid', 'gpo-preference-drive-letter-invalid',
|
|
'gpo-preference-drive-path-invalid', 'gpo-preference-drive-visibility-invalid'
|
|
]));
|
|
expect(reduceGroupPolicyPreferences(document, context)).toEqual([]);
|
|
});
|
|
|
|
it('keeps unknown properties and computer scope partial without inventing mappings', () => {
|
|
const unknown = parseGroupPolicyPreferenceXml(verifiedFile(
|
|
`<Drives clsid="${rootClsid}"><Drive clsid="${itemClsid}" uid="{77777777-7777-4777-8777-777777777777}"><Properties action="U" useLetter="1" letter="S" syntheticMode="1"/></Drive></Drives>`
|
|
), 'user');
|
|
expect(unknown).toMatchObject({ status: 'partial', instructions: [], diagnosticCodes: ['gpo-preference-drive-item-unsupported'] });
|
|
|
|
const computerScoped = parseGroupPolicyPreferenceXml(verifiedFile(
|
|
`<Drives clsid="${rootClsid}"><Drive clsid="${itemClsid}" uid="{88888888-8888-4888-8888-888888888888}"><Properties action="U" useLetter="1" letter="S"/></Drive></Drives>`
|
|
), 'computer');
|
|
expect(computerScoped).toMatchObject({ status: 'partial', instructions: [] });
|
|
expect(computerScoped.diagnosticCodes).toContain('gpo-preference-drives-scope-unsupported');
|
|
expect(reduceGroupPolicyPreferences(computerScoped, context)).toEqual([]);
|
|
});
|
|
|
|
it('rejects malformed persisted Drive Maps observations at the runtime contract', () => {
|
|
const document = parseGroupPolicyPreferenceXml(verifiedFile(
|
|
`<Drives clsid="${rootClsid}"><Drive clsid="${itemClsid}" uid="{99999999-9999-4999-8999-999999999999}"><Properties action="U" useLetter="1" letter="S"/></Drive></Drives>`
|
|
), 'user');
|
|
const observation = structuredClone(reduceGroupPolicyPreferences(document, context)[0]) as any;
|
|
observation.scope = 'computer';
|
|
observation.values[0].selectionMode = 'delete-range';
|
|
observation.values[0].passwordAttributePresent = 'yes';
|
|
expect(gpoPolicyObservationValidationErrors(observation)).toContain('Eine Drive-Map-Preference ist ungültig.');
|
|
});
|
|
});
|