Assessor_2.0/src/domain/gpo-policy-identity.ts

169 lines
6.9 KiB
TypeScript

import type { GpoPolicyScope } from './gpo-ingestion';
export const GPO_POLICY_IDENTITY_RESOLVER = {
id: 'gpo-technical-registry-target',
version: '1.0.0'
} as const;
export const GPO_NATIVE_POLICY_IDENTITY_RESOLVER = {
id: 'gpo-native-policy-target', version: '1.0.0'
} as const;
export const ADMX_POLICY_CATALOG_PARSER = {
id: 'admx-policy-metadata-catalog',
version: '1.0.0'
} as const;
export type ResolvableGpoPolicyScope = Exclude<GpoPolicyScope, 'shared' | 'unknown'>;
export interface GpoRegistryTechnicalTarget {
kind: 'registry-key' | 'registry-value';
hive: 'HKEY_LOCAL_MACHINE' | 'HKEY_CURRENT_USER';
key: string;
valueName?: string;
}
export interface AdmxPolicyRegistryTarget {
version: 1;
scope: ResolvableGpoPolicyScope;
key: string;
match: 'exact-value' | 'key-values';
valueName?: string;
role: 'policy-state' | 'element' | 'enabled-list' | 'disabled-list';
elementId?: string;
}
export interface AdmxPolicyDefinition {
version: 1;
technicalId: string;
targetNamespace: string;
name: string;
class: 'machine' | 'user' | 'both';
displayNameResourceId?: string;
explainTextResourceId?: string;
sourcePath: string;
sourceSha256: string;
registryTargets: AdmxPolicyRegistryTarget[];
}
export interface AdmxLanguageResource {
version: 1;
sourcePath: string;
sourceSha256: string;
admxFileName: string;
locale: string;
strings: Record<string, string>;
}
export interface AdmxPolicyCatalog {
version: 1;
id: string;
parser: typeof ADMX_POLICY_CATALOG_PARSER;
source: { kind: 'microsoft-security-baseline'; baselineId: string; packageSha256: string };
status: 'complete' | 'partial';
policies: AdmxPolicyDefinition[];
languageResources: AdmxLanguageResource[];
diagnosticCodes: string[];
}
export interface AdmxPolicyReference {
technicalId: string;
targetNamespace: string;
policyName: string;
settingId: string;
targetRole: AdmxPolicyRegistryTarget['role'];
}
export type AdmxPolicyResolution =
| { status: 'matched'; catalogId: string; reference: AdmxPolicyReference }
| { status: 'not-found'; catalogId?: string }
| { status: 'ambiguous'; catalogId: string; candidates: AdmxPolicyReference[] };
export interface GpoTechnicalPolicyIdentity {
version: 1;
id: string;
resolver: typeof GPO_POLICY_IDENTITY_RESOLVER | typeof GPO_NATIVE_POLICY_IDENTITY_RESOLVER;
scope: ResolvableGpoPolicyScope;
target: GpoRegistryTechnicalTarget | {
kind: 'security-template' | 'advanced-audit-policy';
section: string;
key: string;
hive?: never;
valueName?: never;
};
admx: AdmxPolicyResolution;
}
export type GpoPolicyIdentityResolution =
| { version: 1; status: 'resolved'; identity: GpoTechnicalPolicyIdentity }
| { version: 1; status: 'unresolved'; reasonCode: string };
function safeTechnicalText(value: string): boolean {
return Boolean(value.trim()) && value.length <= 32_767 && !/[\u0000-\u001f]/.test(value);
}
export function admxPolicyCatalogValidationErrors(catalog: AdmxPolicyCatalog): string[] {
const errors: string[] = [];
if (catalog.version !== 1 || !catalog.id.trim() || !/^[a-f0-9]{64}$/.test(catalog.source.packageSha256)) {
errors.push('Der ADMX-Katalog besitzt keine gültige Version, ID oder Paketprüfsumme.');
}
if (!catalog.source.baselineId.trim() || catalog.parser.id !== ADMX_POLICY_CATALOG_PARSER.id
|| catalog.parser.version !== ADMX_POLICY_CATALOG_PARSER.version) {
errors.push('Der ADMX-Katalog besitzt keine gültige Quelle oder Parserprovenienz.');
}
const policyIds = new Set<string>();
for (const policy of catalog.policies) {
if (policy.version !== 1 || !safeTechnicalText(policy.technicalId) || !safeTechnicalText(policy.targetNamespace)
|| !safeTechnicalText(policy.name) || !safeTechnicalText(policy.sourcePath) || !/^[a-f0-9]{64}$/.test(policy.sourceSha256)
|| policyIds.has(policy.technicalId)) {
errors.push('Der ADMX-Katalog enthält eine ungültige oder doppelte Policydefinition.');
continue;
}
policyIds.add(policy.technicalId);
for (const target of policy.registryTargets) {
if (target.version !== 1 || !safeTechnicalText(target.key)
|| (target.match === 'exact-value' && target.valueName === undefined)
|| (target.match === 'key-values' && target.valueName !== undefined)
|| (target.role === 'element' && !target.elementId?.trim())) {
errors.push('Eine ADMX-Registryzuordnung ist ungültig.');
}
}
}
for (const resource of catalog.languageResources) {
if (resource.version !== 1 || !safeTechnicalText(resource.sourcePath) || !/^[a-f0-9]{64}$/.test(resource.sourceSha256)
|| !safeTechnicalText(resource.admxFileName) || !safeTechnicalText(resource.locale)
|| Object.entries(resource.strings).some(([id, value]) => !safeTechnicalText(id) || typeof value !== 'string')) {
errors.push('Der ADML-Ressourcenkatalog ist ungültig.');
}
}
return [...new Set(errors)];
}
export function gpoTechnicalPolicyIdentityValidationErrors(identity: GpoTechnicalPolicyIdentity): string[] {
const errors: string[] = [];
if (identity.version !== 1 || !/^gpo-policy-identity-[a-f0-9]{32}$/.test(identity.id)
|| ![GPO_POLICY_IDENTITY_RESOLVER.id, GPO_NATIVE_POLICY_IDENTITY_RESOLVER.id].includes(identity.resolver.id)
|| identity.resolver.version !== GPO_POLICY_IDENTITY_RESOLVER.version) {
errors.push('Die technische Policy-Identität besitzt eine ungültige Version oder Resolverprovenienz.');
}
const expectedHive = identity.scope === 'computer' ? 'HKEY_LOCAL_MACHINE' : 'HKEY_CURRENT_USER';
const registry = identity.target.kind === 'registry-key' || identity.target.kind === 'registry-value';
if (!['computer', 'user'].includes(identity.scope)
|| identity.resolver.id !== (registry ? GPO_POLICY_IDENTITY_RESOLVER.id : GPO_NATIVE_POLICY_IDENTITY_RESOLVER.id)
|| !['registry-key', 'registry-value', 'security-template', 'advanced-audit-policy'].includes(identity.target.kind)
|| (registry && identity.target.hive !== expectedHive) || !safeTechnicalText(identity.target.key)
|| (!registry && (!('section' in identity.target) || !safeTechnicalText(identity.target.section) || identity.target.hive !== undefined))
|| (identity.target.kind === 'registry-value' && identity.target.valueName === undefined)
|| (identity.target.kind === 'registry-key' && identity.target.valueName !== undefined)) {
errors.push('Die technische Registry-Zielidentität ist ungültig.');
}
if (identity.admx.status === 'matched' && (!identity.admx.catalogId.trim()
|| !identity.admx.reference.technicalId.trim() || !identity.admx.reference.settingId.trim())) {
errors.push('Die ADMX-Zuordnung der Policy-Identität ist ungültig.');
}
if (identity.admx.status === 'ambiguous' && (!identity.admx.catalogId.trim() || identity.admx.candidates.length < 2)) {
errors.push('Eine mehrdeutige ADMX-Zuordnung benötigt mindestens zwei Kandidaten.');
}
return errors;
}