192 lines
7.9 KiB
TypeScript
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)];
|
|
}
|