507 lines
19 KiB
TypeScript
507 lines
19 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { buildPolicyEvaluation } from '../../server/gpo/gpo-policy-evaluation-service';
|
|
import { buildPolicyFindingRun } from '../../server/gpo/gpo-policy-finding-service';
|
|
import { createPolicyUnifiedResult } from '../../src/domain/unified-result';
|
|
import type { MicrosoftSecurityBaselineRecord } from '../../src/domain/microsoft-security-baseline';
|
|
import type { StoredMicrosoftSecurityBaselineAnalysis } from '../../src/domain/microsoft-security-baseline-analysis';
|
|
import type { GpoRegistryPolicyValue } from '../../src/domain/gpo-policy';
|
|
import type { GpoTechnicalPolicyIdentity } from '../../src/domain/gpo-policy-identity';
|
|
import type { EnvironmentProfile, StoredPolicySnapshotAggregation } from '../../src/domain/gpo-snapshot-aggregation';
|
|
|
|
import type { PolicyEvaluationRule, PolicyEvaluationRuleCatalog } from '../../src/domain/policy-evaluation-rules';
|
|
const timestamp = '2026-08-04T18:00:00.000Z';
|
|
const projectId = 'policy-evaluation-project';
|
|
const identity: GpoTechnicalPolicyIdentity = {
|
|
version: 1,
|
|
id: 'gpo-policy-identity-' + '1'.repeat(32),
|
|
resolver: { id: 'gpo-technical-registry-target', version: '1.0.0' },
|
|
scope: 'computer',
|
|
target: { kind: 'registry-value', hive: 'HKEY_LOCAL_MACHINE', key: 'Software\\Policies\\Synthetic', valueName: 'Mode' },
|
|
admx: { status: 'not-found' }
|
|
};
|
|
const profile: EnvironmentProfile = {
|
|
version: 1,
|
|
id: 'environment-profile-evaluation',
|
|
projectId,
|
|
name: 'Synthetic evaluation profile',
|
|
revision: 1,
|
|
contexts: [{
|
|
version: 1,
|
|
id: 'windows-server-2022',
|
|
kind: 'operating-system',
|
|
productFamily: 'Windows Server',
|
|
productVersion: '2022',
|
|
role: 'Member Server',
|
|
scope: 'computer'
|
|
}],
|
|
createdAt: timestamp
|
|
};
|
|
|
|
function evaluationRule(overrides: Partial<PolicyEvaluationRule> = {}): PolicyEvaluationRule {
|
|
return {
|
|
version: 1,
|
|
id: 'synthetic-policy-evaluation-rule',
|
|
identityId: identity.id,
|
|
deliveryMechanism: 'registry-policy',
|
|
contexts: [{
|
|
version: 1,
|
|
productFamily: 'Windows Server',
|
|
productVersion: '2022',
|
|
roles: ['Member Server'],
|
|
scopes: ['computer']
|
|
}],
|
|
baselineDisposition: 'explicit-recommendation',
|
|
comparator: { kind: 'equality' },
|
|
riskRule: { version: 1, id: 'synthetic-policy-risk', severity: 'high' },
|
|
dependencies: [],
|
|
...overrides
|
|
};
|
|
}
|
|
|
|
function ruleCatalog(rules: PolicyEvaluationRule[] = [evaluationRule()]): PolicyEvaluationRuleCatalog {
|
|
return {
|
|
version: 1,
|
|
id: 'synthetic-policy-evaluation-catalog',
|
|
catalog: { id: 'policy-evaluation-rule-catalog', version: '1.0.0' },
|
|
status: 'released',
|
|
rules
|
|
};
|
|
}
|
|
|
|
function value(number: number): GpoRegistryPolicyValue {
|
|
return {
|
|
kind: 'registry-policy', operation: 'set-value', key: identity.target.key, valueName: 'Mode',
|
|
typeCode: 4, data: { kind: 'uint32', value: number }
|
|
};
|
|
}
|
|
|
|
function aggregation(actual: number, status: 'complete' | 'partial' = 'complete'): StoredPolicySnapshotAggregation {
|
|
return {
|
|
version: 1,
|
|
run: {
|
|
version: 1,
|
|
id: 'policy-snapshot-aggregation-evaluation',
|
|
projectId,
|
|
environmentProfileId: profile.id,
|
|
sourceAnalysisRunIds: ['identity-analysis-evaluation'],
|
|
snapshotId: 'snapshot-evaluation',
|
|
aggregator: { id: 'gpo-policy-snapshot-aggregation', version: '1.0.0' },
|
|
status,
|
|
stateCount: 1,
|
|
conflictCount: 0,
|
|
unresolvedIdentityCount: 0,
|
|
reviewHintCount: 0,
|
|
permitsFindingGeneration: false,
|
|
diagnosticCodes: status === 'complete' ? [] : ['policy-aggregation-snapshot-incomplete'],
|
|
startedAt: timestamp,
|
|
completedAt: timestamp
|
|
},
|
|
snapshot: {
|
|
version: 1,
|
|
id: 'snapshot-evaluation',
|
|
projectId,
|
|
domain: { status: 'resolved', key: 'synthetic.example', evidence: 'backup-metadata' },
|
|
backupSetId: 'set-evaluation',
|
|
instanceIds: ['backup-evaluation'],
|
|
status: 'consistent',
|
|
completeness: status === 'complete' ? 'complete' : 'selected-subset',
|
|
permitsSetAbsenceConclusions: status === 'complete',
|
|
diagnosticCodes: [],
|
|
createdAt: timestamp
|
|
},
|
|
states: [{
|
|
version: 1,
|
|
id: 'aggregated-state-evaluation',
|
|
aggregationRunId: 'policy-snapshot-aggregation-evaluation',
|
|
identity,
|
|
deliveryMechanism: 'registry-policy',
|
|
status: 'configured',
|
|
values: [value(actual)],
|
|
observationIds: ['observation-evaluation'],
|
|
backupInstanceIds: ['backup-evaluation'],
|
|
applicableContextIds: ['windows-server-2022'],
|
|
ordering: 'single-gpo',
|
|
targeting: 'none',
|
|
completeness: 'complete',
|
|
effectiveState: 'unknown',
|
|
diagnosticCodes: []
|
|
}],
|
|
reviewHints: []
|
|
};
|
|
}
|
|
|
|
function baseline(id: string, recommended: number, status: 'complete' | 'partial' = 'complete'):
|
|
{ record: MicrosoftSecurityBaselineRecord; analysis: StoredMicrosoftSecurityBaselineAnalysis } {
|
|
const baselineId = 'baseline-' + id;
|
|
const analysisId = 'baseline-analysis-' + id;
|
|
const packageSha256 = id.padEnd(64, 'a').slice(0, 64).replace(/[^a-f0-9]/g, 'a');
|
|
return {
|
|
record: {
|
|
version: 1,
|
|
id: baselineId,
|
|
fileName: id + '.zip',
|
|
sha256: packageSha256,
|
|
sizeBytes: 100,
|
|
storageKey: 'baseline/' + id,
|
|
importedAt: timestamp,
|
|
status: 'eligible',
|
|
metadata: {
|
|
productContexts: [{ productFamily: 'Windows Server', productVersion: '2022', roles: ['Member Server'], scopes: ['computer'] }],
|
|
roles: ['Member Server'], scopes: ['computer'], source: 'confirmed'
|
|
}
|
|
},
|
|
analysis: {
|
|
version: 1,
|
|
analysis: {
|
|
version: 1,
|
|
id: analysisId,
|
|
baselineId,
|
|
packageSha256,
|
|
analyzer: { id: 'microsoft-baseline-gpo-settings', version: '1.0.0' },
|
|
analyzedAt: timestamp,
|
|
status,
|
|
admxCatalog: {
|
|
version: 1,
|
|
id: 'admx-catalog-' + id,
|
|
parser: { id: 'admx-policy-metadata-catalog', version: '1.0.0' },
|
|
source: { kind: 'microsoft-security-baseline', baselineId, packageSha256 },
|
|
status,
|
|
policies: [],
|
|
languageResources: [],
|
|
diagnosticCodes: status === 'complete' ? [] : ['admx-catalog-partial']
|
|
},
|
|
backupCount: 1,
|
|
registryPolicyFileCount: 1,
|
|
settings: [{
|
|
version: 1,
|
|
id: 'baseline-setting-' + id,
|
|
baselineId,
|
|
backupId: 'baseline-backup-' + id,
|
|
observation: {
|
|
sourcePath: 'GPO/DomainSysvol/GPO/Machine/Registry.pol',
|
|
sourceSha256: 'b'.repeat(64),
|
|
scope: 'computer',
|
|
deliveryMechanism: 'registry-policy',
|
|
parser: { id: 'registry-policy-preg', version: '1.0.0' },
|
|
reducer: { id: 'registry-policy-reducer', version: '1.0.0' },
|
|
state: 'instruction',
|
|
values: [value(recommended)],
|
|
registryPolicy: { sequence: 0, operation: 'set-value' },
|
|
effectiveState: 'unknown',
|
|
diagnosticCodes: []
|
|
},
|
|
identity: { version: 1, status: 'resolved', identity }
|
|
}],
|
|
diagnosticCodes: status === 'complete' ? [] : ['baseline-analysis-partial']
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
function evaluate(actual: number, recommended: number, options: { aggregationStatus?: 'complete' | 'partial'; baselineStatus?: 'complete' | 'partial' } = {}) {
|
|
const source = baseline('primary', recommended, options.baselineStatus);
|
|
return buildPolicyEvaluation({
|
|
profile,
|
|
aggregation: aggregation(actual, options.aggregationStatus),
|
|
baselineRecords: [source.record],
|
|
baselineAnalyses: [source.analysis],
|
|
ruleCatalog: ruleCatalog(),
|
|
startedAt: timestamp,
|
|
completedAt: timestamp
|
|
});
|
|
}
|
|
|
|
function evaluateWithCatalog(
|
|
actual: number,
|
|
recommended: number,
|
|
catalog: PolicyEvaluationRuleCatalog,
|
|
aggregated: StoredPolicySnapshotAggregation = aggregation(actual)
|
|
) {
|
|
const source = baseline('catalog-case', recommended);
|
|
return buildPolicyEvaluation({
|
|
profile,
|
|
aggregation: aggregated,
|
|
baselineRecords: [source.record],
|
|
baselineAnalyses: [source.analysis],
|
|
ruleCatalog: catalog,
|
|
startedAt: timestamp,
|
|
completedAt: timestamp
|
|
});
|
|
}
|
|
|
|
describe('policy baseline evaluation', () => {
|
|
it('distinguishes an exact match from a confirmed configured deviation', () => {
|
|
expect(evaluate(1, 1)).toMatchObject({
|
|
run: { status: 'complete', matchCount: 1, deviationCount: 0, projectedFindingCount: 0 },
|
|
results: [{ classification: 'configured_match', findingEligibility: 'ineligible', effectiveState: 'unknown' }]
|
|
});
|
|
expect(evaluate(2, 1)).toMatchObject({
|
|
run: { status: 'complete', matchCount: 0, deviationCount: 1, projectedFindingCount: 0 },
|
|
results: [{ classification: 'confirmed_configured_deviation', findingEligibility: 'eligible', effectiveState: 'unknown' }]
|
|
});
|
|
});
|
|
|
|
it('turns incomplete customer evidence into review and never into an eligible deviation', () => {
|
|
const record = evaluate(2, 1, { aggregationStatus: 'partial' });
|
|
expect(record.run).toMatchObject({ status: 'partial', deviationCount: 0, reviewCount: 1, projectedFindingCount: 0 });
|
|
expect(record.results[0]).toMatchObject({
|
|
classification: 'review_required',
|
|
findingEligibility: 'ineligible',
|
|
diagnosticCodes: expect.arrayContaining(['policy-evaluation-snapshot-incomplete'])
|
|
});
|
|
});
|
|
|
|
it('turns a partial baseline catalog into review', () => {
|
|
const record = evaluate(2, 1, { baselineStatus: 'partial' });
|
|
expect(record.results[0]).toMatchObject({
|
|
classification: 'review_required',
|
|
findingEligibility: 'ineligible',
|
|
diagnosticCodes: expect.arrayContaining(['policy-evaluation-baseline-incomplete'])
|
|
});
|
|
});
|
|
|
|
it('keeps a conflicting customer state as review', () => {
|
|
const source = baseline('conflict', 1);
|
|
const conflicted = aggregation(2);
|
|
conflicted.states[0].status = 'conflicted';
|
|
conflicted.states[0].values = [value(1), value(2)];
|
|
conflicted.run.conflictCount = 1;
|
|
const record = buildPolicyEvaluation({
|
|
profile, aggregation: conflicted, baselineRecords: [source.record], baselineAnalyses: [source.analysis],
|
|
ruleCatalog: ruleCatalog(),
|
|
startedAt: timestamp, completedAt: timestamp
|
|
});
|
|
expect(record.results[0]).toMatchObject({
|
|
classification: 'review_required', findingEligibility: 'ineligible',
|
|
diagnosticCodes: expect.arrayContaining(['policy-evaluation-customer-conflict'])
|
|
});
|
|
});
|
|
|
|
it('keeps a missing applicable baseline recommendation as review', () => {
|
|
const source = baseline('wrong-context', 1);
|
|
source.record.metadata.productContexts![0].productVersion = '2019';
|
|
const record = buildPolicyEvaluation({
|
|
ruleCatalog: ruleCatalog(),
|
|
profile, aggregation: aggregation(2), baselineRecords: [source.record], baselineAnalyses: [source.analysis],
|
|
startedAt: timestamp, completedAt: timestamp
|
|
});
|
|
expect(record.results[0]).toMatchObject({
|
|
classification: 'review_required', findingEligibility: 'ineligible',
|
|
diagnosticCodes: expect.arrayContaining(['policy-evaluation-baseline-missing'])
|
|
});
|
|
});
|
|
|
|
it('requires exactly one matching baseline per context', () => {
|
|
const first = baseline('first', 1);
|
|
const second = baseline('second', 1);
|
|
const record = buildPolicyEvaluation({
|
|
profile,
|
|
aggregation: aggregation(2),
|
|
baselineRecords: [first.record, second.record],
|
|
ruleCatalog: ruleCatalog(),
|
|
baselineAnalyses: [first.analysis, second.analysis],
|
|
startedAt: timestamp,
|
|
completedAt: timestamp
|
|
});
|
|
expect(record.results[0]).toMatchObject({
|
|
classification: 'review_required',
|
|
findingEligibility: 'ineligible',
|
|
diagnosticCodes: expect.arrayContaining(['policy-evaluation-baseline-ambiguous'])
|
|
});
|
|
});
|
|
|
|
it('requires an explicit setting-specific released rule instead of an implicit equality fallback', () => {
|
|
const record = evaluateWithCatalog(2, 1, ruleCatalog([]));
|
|
expect(record.results[0]).toMatchObject({
|
|
classification: 'review_required',
|
|
baselineState: 'catalog-gap',
|
|
findingEligibility: 'ineligible',
|
|
ruleIds: [],
|
|
diagnosticCodes: expect.arrayContaining(['policy-evaluation-rule-catalog-gap'])
|
|
});
|
|
});
|
|
|
|
it('uses an explicitly bound minimum comparator and a released risk rule', () => {
|
|
const catalog = ruleCatalog([evaluationRule({ comparator: { kind: 'minimum' } })]);
|
|
expect(evaluateWithCatalog(2, 1, catalog).results[0]).toMatchObject({
|
|
classification: 'configured_match',
|
|
baselineState: 'explicit-recommendation',
|
|
comparator: { relation: 'stronger', comparator: { kind: 'minimum' } },
|
|
riskRuleIds: ['synthetic-policy-risk'],
|
|
findingEligibility: 'ineligible'
|
|
});
|
|
expect(evaluateWithCatalog(0, 1, catalog).results[0]).toMatchObject({
|
|
classification: 'confirmed_configured_deviation',
|
|
comparator: { relation: 'weaker' },
|
|
findingEligibility: 'eligible'
|
|
});
|
|
});
|
|
|
|
it('persists an explicit not-applicable baseline state without a comparator or finding eligibility', () => {
|
|
const catalog = ruleCatalog([evaluationRule({
|
|
baselineDisposition: 'explicitly-not-applicable',
|
|
comparator: undefined,
|
|
riskRule: undefined
|
|
})]);
|
|
const record = evaluateWithCatalog(2, 1, catalog);
|
|
expect(record.run).toMatchObject({ status: 'complete', notApplicableCount: 1, projectedFindingCount: 0 });
|
|
expect(record.results[0]).toMatchObject({
|
|
classification: 'not_applicable',
|
|
baselineState: 'explicitly-not-applicable',
|
|
baselineSettingIds: [],
|
|
riskRuleIds: [],
|
|
findingEligibility: 'ineligible',
|
|
effectiveState: 'unknown'
|
|
});
|
|
});
|
|
|
|
it('treats a missing prerequisite as not applicable only for a complete absence-capable snapshot', () => {
|
|
const catalog = ruleCatalog([evaluationRule({ dependencies: [{
|
|
version: 1,
|
|
id: 'synthetic-prerequisite',
|
|
kind: 'prerequisite',
|
|
identityId: 'gpo-policy-identity-' + '2'.repeat(32),
|
|
deliveryMechanism: 'registry-policy'
|
|
}] })]);
|
|
expect(evaluateWithCatalog(2, 1, catalog).results[0]).toMatchObject({
|
|
classification: 'not_applicable',
|
|
baselineState: 'explicitly-not-applicable',
|
|
dependencyRuleIds: ['synthetic-prerequisite'],
|
|
findingEligibility: 'ineligible'
|
|
});
|
|
const incomplete = aggregation(2, 'partial');
|
|
incomplete.snapshot.permitsSetAbsenceConclusions = false;
|
|
expect(evaluateWithCatalog(2, 1, catalog, incomplete).results[0]).toMatchObject({
|
|
classification: 'review_required',
|
|
findingEligibility: 'ineligible',
|
|
diagnosticCodes: expect.arrayContaining([
|
|
'policy-dependency-target-absence-unknown',
|
|
'policy-evaluation-snapshot-incomplete'
|
|
])
|
|
});
|
|
});
|
|
|
|
it('keeps an active mutual exclusion as review instead of inventing precedence', () => {
|
|
const otherIdentity = { ...identity, id: 'gpo-policy-identity-' + '3'.repeat(32) };
|
|
const aggregated = aggregation(2);
|
|
aggregated.states.push({
|
|
...aggregated.states[0],
|
|
id: 'aggregated-state-mutual-exclusion',
|
|
identity: otherIdentity
|
|
});
|
|
aggregated.run.stateCount = 2;
|
|
const catalog = ruleCatalog([evaluationRule({ dependencies: [{
|
|
version: 1,
|
|
id: 'synthetic-mutual-exclusion',
|
|
kind: 'mutual-exclusion',
|
|
identityId: otherIdentity.id,
|
|
deliveryMechanism: 'registry-policy'
|
|
}] })]);
|
|
expect(evaluateWithCatalog(2, 1, catalog, aggregated).results.find(result => result.identity.id === identity.id)).toMatchObject({
|
|
classification: 'review_required',
|
|
findingEligibility: 'ineligible',
|
|
diagnosticCodes: expect.arrayContaining(['policy-dependency-mutual-exclusion-active'])
|
|
});
|
|
});
|
|
|
|
it('applies an explicit override as not applicable when the overriding state is configured', () => {
|
|
const otherIdentity = { ...identity, id: 'gpo-policy-identity-' + '4'.repeat(32) };
|
|
const aggregated = aggregation(2);
|
|
aggregated.states.push({
|
|
...aggregated.states[0],
|
|
id: 'aggregated-state-override',
|
|
identity: otherIdentity
|
|
});
|
|
aggregated.run.stateCount = 2;
|
|
const catalog = ruleCatalog([evaluationRule({ dependencies: [{
|
|
version: 1,
|
|
id: 'synthetic-override',
|
|
kind: 'override',
|
|
identityId: otherIdentity.id,
|
|
deliveryMechanism: 'registry-policy'
|
|
}] })]);
|
|
expect(evaluateWithCatalog(2, 1, catalog, aggregated).results.find(result => result.identity.id === identity.id)).toMatchObject({
|
|
classification: 'not_applicable',
|
|
baselineState: 'explicitly-not-applicable',
|
|
dependencyRuleIds: ['synthetic-override'],
|
|
findingEligibility: 'ineligible'
|
|
});
|
|
});
|
|
|
|
it('keeps an explicit no-recommendation state distinct and review-only', () => {
|
|
const catalog = ruleCatalog([evaluationRule({
|
|
baselineDisposition: 'no-recommendation',
|
|
comparator: undefined,
|
|
riskRule: undefined
|
|
})]);
|
|
const record = evaluateWithCatalog(2, 1, catalog);
|
|
expect(record.results[0]).toMatchObject({
|
|
classification: 'review_required',
|
|
baselineState: 'no-recommendation',
|
|
baselineSettingIds: [],
|
|
findingEligibility: 'ineligible',
|
|
diagnosticCodes: expect.arrayContaining(['policy-evaluation-no-recommendation'])
|
|
});
|
|
});
|
|
|
|
it('creates a policy finding only for a fully evidenced eligible configured deviation', () => {
|
|
const record = buildPolicyFindingRun({
|
|
evaluation: evaluate(2, 1),
|
|
aggregation: aggregation(2),
|
|
profile,
|
|
startedAt: timestamp,
|
|
completedAt: timestamp
|
|
});
|
|
expect(record).toMatchObject({
|
|
run: { status: 'complete', eligibleResultCount: 1, findingCount: 1, rejectedResultCount: 0 },
|
|
occurrences: [{
|
|
classification: 'confirmed_configured_deviation',
|
|
outcome: 'fail',
|
|
severity: 'high',
|
|
scopeState: 'unknown',
|
|
effectiveState: 'not-verified'
|
|
}],
|
|
rejections: []
|
|
});
|
|
const projected = createPolicyUnifiedResult({
|
|
definition: record.definitions[0],
|
|
occurrence: record.occurrences[0]
|
|
});
|
|
expect(projected).toMatchObject({
|
|
sourceKind: 'policy-evaluation',
|
|
resultType: 'policy_deviation',
|
|
outcomeNormalized: 'fail',
|
|
isIoe: false,
|
|
isDeviation: true,
|
|
effectiveState: 'unknown_effective_state',
|
|
scopeState: 'unknown',
|
|
effectivenessVerification: 'not-verified'
|
|
});
|
|
expect(projected).not.toHaveProperty('assessmentId');
|
|
});
|
|
|
|
it('never creates a finding when eligible evidence is incomplete or the result is not a deviation', () => {
|
|
const incomplete = structuredClone(evaluate(2, 1));
|
|
incomplete.evidence = incomplete.evidence.filter(item => item.kind !== 'baseline-setting');
|
|
const rejected = buildPolicyFindingRun({
|
|
evaluation: incomplete,
|
|
aggregation: aggregation(2),
|
|
profile,
|
|
startedAt: timestamp,
|
|
completedAt: timestamp
|
|
});
|
|
expect(rejected).toMatchObject({
|
|
run: { status: 'partial', eligibleResultCount: 1, findingCount: 0, rejectedResultCount: 1 },
|
|
occurrences: [],
|
|
rejections: [{ diagnosticCodes: expect.arrayContaining(['policy-finding-evidence-incomplete']) }]
|
|
});
|
|
|
|
const matched = buildPolicyFindingRun({
|
|
evaluation: evaluate(1, 1), aggregation: aggregation(1), profile,
|
|
startedAt: timestamp, completedAt: timestamp
|
|
});
|
|
expect(matched.run).toMatchObject({ status: 'complete', eligibleResultCount: 0, findingCount: 0 });
|
|
expect(matched.occurrences).toEqual([]);
|
|
});
|
|
});
|