86 lines
5.0 KiB
TypeScript
86 lines
5.0 KiB
TypeScript
import type { ToolProfileDefinition } from '../../domain/tool-profile-definition';
|
|
import { commonSeverityMappings, commonStatusMappings } from '../common-normalization';
|
|
import { genericFieldRules } from '../generic/generic-field-rules';
|
|
|
|
export const maesterDefinition: ToolProfileDefinition = {
|
|
schemaVersion: 1,
|
|
id: 'maester',
|
|
securityScoring: {
|
|
version: '1.0.0',
|
|
native: { kind: 'unavailable', reason: 'Supported Maester reports expose result counts, without a native numeric overall score.' },
|
|
targets: ['entra.tenant_id'], resultPopulation: 'complete'
|
|
},
|
|
priorityBasis: {
|
|
version: '1.0.0',
|
|
// Native severity per test. `Result` beside `Status` stays evidence text and
|
|
// never becomes a rank.
|
|
basis: { kind: 'severity', properties: ['test.severity'] },
|
|
excludedProperties: [
|
|
{ property: 'test.result', reason: 'Descriptive result text, not a criticality.' }
|
|
]
|
|
},
|
|
timeslotComparison: {
|
|
version: '1.0.0', kind: 'available', granularity: 'scope',
|
|
scope: { kind: 'entra-tenant', properties: ['entra.tenant_id'] },
|
|
observationTimeProperties: ['report.generated_at'],
|
|
resultEvidence: 'explicit-results', negativeEvidence: 'unsupported',
|
|
diagnosticPolicy: 'fail-closed-by-impact'
|
|
},
|
|
name: 'Maester',
|
|
version: '1.1.0',
|
|
processing: {
|
|
mappingVersion: '1.1.0', normalizationVersion: '1.0.0', presentationVersion: '1.1.0',
|
|
compatibilityKey: 'maester-canonical-v1', changeClass: 'compatible',
|
|
reimportRequired: true, affectedDimensions: ['evidence', 'environment-context', 'presentation']
|
|
},
|
|
sourceSignals: [
|
|
{ scope: 'file-name', pattern: /maester|test[\s_.-]*results?/i, contribution: 0.25, message: 'Der Dateiname entspricht einem Maester-Testexport.' },
|
|
{ scope: 'raw-text', pattern: /maester\s+security\s+tests/i, contribution: 0.55, message: 'Der Dateiinhalt enthält „Maester Security Tests“.' },
|
|
{ scope: 'raw-text', pattern: /maester\.dev/i, contribution: 0.35, message: 'Der Dateiinhalt enthält eine Maester-Referenz.' },
|
|
{ scope: 'header', pattern: /^testscriptfile$/i, contribution: 0.24, message: 'Die Kopfzeile „TestScriptFile“ wurde gefunden.' },
|
|
{ scope: 'header', pattern: /^resultdetail$/i, contribution: 0.16, message: 'Die Kopfzeile „ResultDetail“ wurde gefunden.' },
|
|
{ scope: 'header', pattern: /^testskipped$/i, contribution: 0.13, message: 'Die Kopfzeile „TestSkipped“ wurde gefunden.' },
|
|
{ scope: 'cell-value', pattern: /^MT\.\d+/i, contribution: 0.3, message: 'Ein Maester-Testbezeichner wurde gefunden.' }
|
|
],
|
|
tableRules: {
|
|
preferredNames: [{ pattern: /test|result|finding/i, contribution: 0.7, message: 'Maester-Testresultate.' }],
|
|
discouragedNames: []
|
|
},
|
|
fieldRules: [
|
|
...genericFieldRules.filter(rule => rule.role !== 'status'),
|
|
{ role: 'status', headers: [{ pattern: /^(?:status|result|outcome|evaluation|zustand)$/i, contribution: 0.96, message: 'Native Maester status field.' }], whenHeadersAbsent: [/^status$/i] },
|
|
{ role: 'status', headers: [{ pattern: /^(?:status|outcome|evaluation|zustand)$/i, contribution: 0.96, message: 'Separate Maester status column.' }], whenHeadersPresent: [/^status$/i] },
|
|
{ role: 'evidence', headers: [{ pattern: /^result$/i, contribution: 1, message: 'Maester result text next to a separate Status column.' }], whenHeadersPresent: [/^status$/i] }
|
|
],
|
|
normalization: { severity: commonSeverityMappings, status: commonStatusMappings },
|
|
presentation: {
|
|
titleHeaders: [/^title$/i],
|
|
severityHeaders: [/^severity$/i],
|
|
sortHeaders: [/^severity$/i],
|
|
sortDirection: 'desc',
|
|
// Der Maester-Excel-Export legt die Testergebnismeldung in eine Spalte
|
|
// "Result", waehrend der Status separat in "Status" steht. Ohne diese
|
|
// Angabe wuerde der Reader "Result" als reines Status-Badge einstufen
|
|
// (Purple-Knight-Konvention) und den einzigen Beleg fuer das Finding
|
|
// vollstaendig ausblenden.
|
|
resultMessageHeaders: [/^resultdetail$/i, /^result$/i],
|
|
statusHeaders: [/^status$/i, /^result$/i],
|
|
// Standardansicht zeigt Abweichungen, nicht bestandene Pruefungen.
|
|
// `deviation` statt `failed`, weil Maester neben Failed auch `Investigate`
|
|
// meldet: eine Pruefung, die nicht automatisch entscheidbar war und fachlich
|
|
// beurteilt werden muss. Mit `failed` als Vorgabe fielen diese Funde aus der
|
|
// Standardansicht heraus. Die Filter `failed` und `passed` bleiben unveraendert
|
|
// erhalten und trennen Pass und Fail weiterhin exakt.
|
|
defaultStatusFilter: 'deviation',
|
|
statusFilters: [
|
|
{ value: 'all', label: 'All Results', patterns: [] },
|
|
{ value: 'deviation', label: 'Deviation', patterns: [/^fail(?:ed)?$/i, /^investigate$/i] },
|
|
{ value: 'failed', label: 'Failed', patterns: [/^fail(?:ed)?$/i] },
|
|
{ value: 'investigate', label: 'Investigate', patterns: [/^investigate$/i] },
|
|
{ value: 'passed', label: 'Passed', patterns: [/^pass(?:ed)?$/i] },
|
|
{ value: 'skipped', label: 'Skipped', patterns: [/^skipped$/i, /^notrun$/i] }
|
|
]
|
|
},
|
|
comparisonKeys: [['rule-id', 'asset'], ['rule-id']]
|
|
};
|