Assessor_2.0/src/domain/gpo-snapshot-aggregation.ts

192 lines
7.9 KiB
TypeScript

import type { GpoAnalysisSnapshot } from './gpo-ingestion';
import type { GpoPolicyObservation, GpoPolicyValue } from './gpo-policy';
import type { GpoTechnicalPolicyIdentity } from './gpo-policy-identity';
import { gpoTechnicalPolicyIdentityValidationErrors } from './gpo-policy-identity';
export const GPO_POLICY_SNAPSHOT_AGGREGATOR = {
id: 'gpo-policy-snapshot-aggregation',
version: '1.1.0'
} as const;
export type EnvironmentProfileScope = 'computer' | 'user' | 'mixed';
export interface EnvironmentProfileContext {
version: 1;
id: string;
kind: 'operating-system' | 'product';
productFamily: string;
productVersion: string;
role: string;
scope: EnvironmentProfileScope;
sourceObservationIds?: string[];
}
export interface EnvironmentProfile {
version: 1;
id: string;
projectId: string;
name: string;
revision: number;
supersedesProfileId?: string;
domainKey?: string;
contexts: EnvironmentProfileContext[];
createdAt: string;
}
export interface CreateEnvironmentProfileRequest {
name: string;
supersedesProfileId?: string;
domainKey?: string;
contexts: Array<Omit<EnvironmentProfileContext, 'version'>>;
}
export type AggregatedPolicyStateStatus = 'configured' | 'deleted' | 'conflicted' | 'indeterminate';
export interface AggregatedPolicyState {
version: 1;
id: string;
aggregationRunId: string;
identity: GpoTechnicalPolicyIdentity;
deliveryMechanism: GpoPolicyObservation['deliveryMechanism'];
status: AggregatedPolicyStateStatus;
values: GpoPolicyValue[];
observationIds: string[];
backupInstanceIds: string[];
applicableContextIds: string[];
ordering: 'single-gpo' | 'cross-gpo-identical' | 'cross-gpo-conflict' | 'indeterminate';
targeting: 'none' | 'unknown';
completeness: 'complete' | 'partial';
effectiveState: 'unknown';
diagnosticCodes: string[];
}
export interface PolicyAggregationReviewHint {
version: 1;
id: string;
code: string;
identityId?: string;
observationIds: string[];
backupInstanceIds: string[];
}
export interface PolicySnapshotAggregationRun {
version: 1;
id: string;
projectId: string;
environmentProfileId: string;
sourceAnalysisRunIds: string[];
snapshotId: string;
aggregator: typeof GPO_POLICY_SNAPSHOT_AGGREGATOR;
status: 'complete' | 'partial' | 'blocked';
stateCount: number;
conflictCount: number;
unresolvedIdentityCount: number;
reviewHintCount: number;
permitsFindingGeneration: false;
diagnosticCodes: string[];
startedAt: string;
completedAt: string;
}
export interface StoredPolicySnapshotAggregation {
version: 1;
run: PolicySnapshotAggregationRun;
snapshot: GpoAnalysisSnapshot;
states: AggregatedPolicyState[];
reviewHints: PolicyAggregationReviewHint[];
}
export interface CreatePolicySnapshotAggregationRequest {
environmentProfileId: string;
sourceAnalysisRunIds: string[];
}
function validText(value: unknown): value is string {
return typeof value === 'string' && Boolean(value.trim()) && value.length <= 32_767 && !/[\u0000-\u001f]/.test(value);
}
function uniqueSorted(values: string[]): boolean {
return new Set(values).size === values.length
&& values.every((value, index) => index === 0 || values[index - 1].localeCompare(value) < 0);
}
export function environmentProfileValidationErrors(profile: EnvironmentProfile): string[] {
const errors: string[] = [];
if (profile.version !== 1 || !validText(profile.id) || !validText(profile.projectId) || !validText(profile.name)
|| !Number.isSafeInteger(profile.revision) || profile.revision < 1 || !Number.isFinite(Date.parse(profile.createdAt))) {
errors.push('Das Environment Profile besitzt keine gueltige Identitaet, Revision oder Zeitangabe.');
}
if (profile.supersedesProfileId === profile.id) errors.push('Ein Environment Profile darf sich nicht selbst ersetzen.');
if (profile.domainKey !== undefined && !validText(profile.domainKey)) errors.push('Der optionale Domaenenschluessel ist ungueltig.');
const contexts = Array.isArray(profile.contexts) ? profile.contexts : [];
if (!contexts.length || !contexts.some(context => context?.kind === 'operating-system')) {
errors.push('Ein Environment Profile benoetigt mindestens einen Betriebssystemkontext.');
}
const contextIds = new Set<string>();
for (const context of contexts) {
if (!context || context.version !== 1 || !['operating-system', 'product'].includes(context.kind)
|| !['computer', 'user', 'mixed'].includes(context.scope)
|| !validText(context.id) || !validText(context.productFamily)
|| !validText(context.productVersion) || !validText(context.role) || contextIds.has(context.id)) {
errors.push('Das Environment Profile enthaelt einen ungueltigen oder doppelten Produktkontext.');
}
contextIds.add(context.id);
if (context.sourceObservationIds !== undefined && (!Array.isArray(context.sourceObservationIds)
|| !context.sourceObservationIds.length || !context.sourceObservationIds.every(validText)
|| new Set(context.sourceObservationIds).size !== context.sourceObservationIds.length)) {
errors.push('Die Quellenreferenzen eines Environment-Kontexts sind ungueltig.');
}
}
return [...new Set(errors)];
}
export function storedPolicySnapshotAggregationValidationErrors(record: StoredPolicySnapshotAggregation): string[] {
const errors: string[] = [];
const { run, snapshot, states, reviewHints } = record;
if (record.version !== 1 || run.version !== 1 || !validText(run.id) || !validText(run.projectId)
|| !validText(run.environmentProfileId) || !validText(run.snapshotId) || run.snapshotId !== snapshot.id
|| snapshot.projectId !== run.projectId) {
errors.push('Der Policy-Snapshot-Aggregationslauf besitzt ungueltige Referenzen.');
}
if (run.aggregator.id !== GPO_POLICY_SNAPSHOT_AGGREGATOR.id
|| run.aggregator.version !== GPO_POLICY_SNAPSHOT_AGGREGATOR.version) {
errors.push('Der Policy-Snapshot-Aggregationslauf benoetigt versionierte Aggregatorprovenienz.');
}
if (!run.sourceAnalysisRunIds.length || !uniqueSorted(run.sourceAnalysisRunIds)) {
errors.push('Quellanalyselaeufe muessen eindeutig und deterministisch sortiert sein.');
}
if (![run.stateCount, run.conflictCount, run.unresolvedIdentityCount, run.reviewHintCount]
.every(value => Number.isSafeInteger(value) && value >= 0)
|| run.stateCount !== states.length || run.conflictCount !== states.filter(state => state.status === 'conflicted').length
|| run.reviewHintCount !== reviewHints.length || run.permitsFindingGeneration !== false) {
errors.push('Die Zaehler oder Finding-Grenze des Policy-Snapshot-Aggregationslaufs sind inkonsistent.');
}
if (![run.startedAt, run.completedAt].every(value => Number.isFinite(Date.parse(value)))
|| Date.parse(run.completedAt) < Date.parse(run.startedAt)) {
errors.push('Die Laufzeit des Policy-Snapshot-Aggregationslaufs ist ungueltig.');
}
const stateIds = new Set<string>();
const stateKeys = new Set<string>();
for (const state of states) {
const key = state.identity.id + '\0' + state.deliveryMechanism;
if (state.version !== 1 || !validText(state.id) || state.aggregationRunId !== run.id
|| stateIds.has(state.id) || stateKeys.has(key) || !state.values.length
|| !uniqueSorted(state.observationIds) || !uniqueSorted(state.backupInstanceIds)
|| !uniqueSorted(state.applicableContextIds) || state.effectiveState !== 'unknown') {
errors.push('Ein aggregierter Policyzustand ist ungueltig oder doppelt.');
}
stateIds.add(state.id);
stateKeys.add(key);
errors.push(...gpoTechnicalPolicyIdentityValidationErrors(state.identity));
}
const hintIds = new Set<string>();
for (const hint of reviewHints) {
if (hint.version !== 1 || !validText(hint.id) || !validText(hint.code) || hintIds.has(hint.id)
|| !uniqueSorted(hint.observationIds) || !uniqueSorted(hint.backupInstanceIds)) {
errors.push('Ein Review-Hinweis der Policyaggregation ist ungueltig oder doppelt.');
}
hintIds.add(hint.id);
}
return [...new Set(errors)];
}