Assessor_2.0/tests/server/microsoft-security-baseline...

104 lines
6.1 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { analyzeMicrosoftSecurityBaselinePackage } from '../../server/gpo/microsoft-security-baseline-parser';
import { syntheticMicrosoftBaselineZip } from './microsoft-baseline-synthetic-fixture';
import { syntheticGpoBackupZip } from './gpo-synthetic-fixture';
import { analyzeGpoBackupForPersistence } from '../../server/gpo/gpo-inventory-executor';
import { buildAdmxPolicyCatalog } from '../../server/gpo/admx-policy-catalog';
import { readMicrosoftBaselineZip } from '../../server/gpo/microsoft-baseline-zip-reader';
describe('Microsoft Security Baseline settings parser', () => {
it('gives reprocessed analyses their own catalog identity without changing catalog contents or policy targets', () => {
const bytes = syntheticMicrosoftBaselineZip();
const options = { baselineId: 'synthetic-baseline', analyzedAt: '2026-09-08T08:00:00Z' };
const archive = readMicrosoftBaselineZip(bytes, entry => /\.(admx|adml)$/i.test(entry.relativePath));
const legacyCatalog = buildAdmxPolicyCatalog(archive.files, {
kind: 'microsoft-security-baseline', baselineId: options.baselineId, packageSha256: archive.containerSha256
});
const analysis = analyzeMicrosoftSecurityBaselinePackage(bytes, options);
expect(analysis.admxCatalog.id).not.toBe(legacyCatalog.id);
expect({ ...analysis.admxCatalog, id: legacyCatalog.id }).toEqual(legacyCatalog);
expect(analysis.settings[0].identity).toMatchObject({
status: 'resolved', identity: { admx: { catalogId: analysis.admxCatalog.id } }
});
expect(analyzeMicrosoftSecurityBaselinePackage(bytes, options)).toEqual(analysis);
});
it.each([
['unsupported services', '[Service General Setting]\n"SyntheticService",2,""', 'gpo-security-template-section-unsupported'],
['malformed supported entry', '[Registry Values]\nMACHINE\\Synthetic=not-a-type', 'gpo-security-template-registry-value-invalid']
])('preserves supported INF settings through customer and baseline analysis: %s', (_name, middle, diagnostic) => {
const bytes = syntheticGpoBackupZip([
'[System Access]', 'MinimumPasswordLength=14', middle,
'[Privilege Rights]', 'SeNetworkLogonRight=*S-1-5-32-544',
'[Registry Values]', 'MACHINE\\Software\\Synthetic\\Mode=4,1'
].join('\n'));
const analysis = analyzeMicrosoftSecurityBaselinePackage(bytes, {
baselineId: 'synthetic-baseline', analyzedAt: '2026-09-08T08:00:00Z'
});
const customer = analyzeGpoBackupForPersistence({ bytes, options: {
projectId: 'synthetic-project', sourceArtifactId: 'synthetic-source', discoveredAt: '2026-09-08T08:00:00Z'
} });
expect(analysis.status).toBe('partial');
expect(analysis.diagnosticCodes).toContain(diagnostic);
expect(analysis.settings).toHaveLength(5);
const templates = analysis.settings.filter(item => item.observation.deliveryMechanism === 'security-template');
expect(templates).toHaveLength(3);
expect(templates.every(item => item.observation.parser.version === '1.1.0')).toBe(true);
for (const setting of templates) {
expect(setting.identity.status).toBe('resolved');
if (setting.identity.status !== 'resolved') continue;
const identityId = setting.identity.identity.id;
expect(customer.identityResolutions.some(item => item.resolution.status === 'resolved'
&& item.resolution.identity.id === identityId)).toBe(true);
}
expect(customer.diagnosticCodes).toContain(diagnostic);
expect(analysis.diagnosticCodes).not.toContain('gpo-security-template-entry-invalid');
});
it('uses identical native targets for customer and baseline security templates and audit policy', () => {
const bytes = syntheticGpoBackupZip();
const customer = analyzeGpoBackupForPersistence({ bytes, options: {
projectId: 'synthetic', sourceArtifactId: 'synthetic-source', discoveredAt: '2026-09-06T12:00:00Z'
} });
const baseline = analyzeMicrosoftSecurityBaselinePackage(bytes, { baselineId: 'synthetic-baseline', analyzedAt: '2026-09-06T12:00:00Z' });
expect(baseline.settings).toHaveLength(3);
for (const setting of baseline.settings) {
expect(setting.identity.status).toBe('resolved');
if (setting.identity.status !== 'resolved') continue;
const id = setting.identity.identity.id;
expect(customer.identityResolutions.some(item => item.resolution.status === 'resolved' && item.resolution.identity.id === id)).toBe(true);
}
expect(baseline.settings.map(item => item.observation.deliveryMechanism)).toEqual(expect.arrayContaining(['security-template', 'advanced-audit-policy']));
});
it('uses the Registry.pol parser and stable ADMX identity without opening nested documentation', () => {
const bytes = syntheticMicrosoftBaselineZip();
const analysis = analyzeMicrosoftSecurityBaselinePackage(bytes, {
baselineId: 'synthetic-baseline', analyzedAt: '2026-08-04T12:00:00.000Z'
});
expect(analysis).toMatchObject({
status: 'complete',
backupCount: 1,
registryPolicyFileCount: 1,
settings: [{
observation: {
parser: { id: 'gpo-registry-policy-binary', version: '1.0.0' },
reducer: { id: 'gpo-registry-policy-instruction-reducer', version: '1.0.0' },
values: [{ kind: 'registry-policy', operation: 'set-value', key: 'Software\\Policies\\Synthetic', valueName: 'Mode' }]
},
identity: {
status: 'resolved',
identity: { admx: { status: 'matched', reference: { technicalId: 'Synthetic.Policies#Mode', settingId: '$policy' } } }
}
}]
});
expect(analysis.settings[0].observation.sourceRecords).toEqual([{ index: 0, byteOffset: 8 }]);
});
it('fails closed through the shared Registry.pol parser on an unknown type', () => {
const analysis = analyzeMicrosoftSecurityBaselinePackage(syntheticMicrosoftBaselineZip(99), {
baselineId: 'synthetic-baseline', analyzedAt: '2026-08-04T12:00:00.000Z'
});
expect(analysis.status).toBe('partial');
expect(analysis.settings).toEqual([]);
expect(analysis.diagnosticCodes).toContain('gpo-registry-policy-type-unsupported');
});
});