Wir haben jede Constraint in OSCAL 1.2.2 gezählt. Alle 348.
Jeder OSCAL-Validator behauptet, „OSCAL zu validieren". Kaum einer kann sagen, was dieser Satz bedeutet.
NISTs OSCAL-Modelle sind in Metaschema verfasst – acht Root-Module plus gemeinsame Importe, tausende Zeilen XML, die nicht nur die Form von Dokumenten definieren, sondern ihre Semantik: erlaubte Wertevokabulare, Eindeutigkeitsregeln, Querverweis-Indizes, Kardinalitätsanforderungen. Ein JSON-Schema fängt die Form. Die Semantik ist der Ort, an dem Compliance-Dokumente tatsächlich schiefgehen – und an dem aus „wir validieren OSCAL" leise „wir validieren einen Teil von OSCAL, wir sind nicht sicher welchen" wird.
Wir wollten sicher sein, welchen Teil. Also haben wir unseren Validator so gebaut, wie man ein System auditiert: bei den Quellen anfangen, alles enumerieren und jeden einzelnen Posten erklären.
Den Standard auf Bytes pinnen
Die erste Entscheidung war die wichtigste: Die NIST-Quellen sind die Autorität – nicht die Referenzimplementierung. Wir haben usnistgov/OSCAL beim Commit 21403b4a… (v1.2.2) gepinnt, jedes Metaschema byte-exakt mit SHA-256-Hashes gecacht und das altgediente Java OSCAL CLI als das behandelt, was es tatsächlich ist: ein Komparator zum Kreuzverhör, keine Wahrheit zum Abschreiben. (Es bettet zum Beispiel OSCAL-1.2.1-Bindings ein – es kann 1.2.2-Dokumente verarbeiten, aber es spricht kein 1.2.2.)
Dann haben wir jede Constraint-Occurrence in den gepinnten Quellen lexikalisch inventarisiert. Nicht Constraint-Typen – Occurrences, identifiziert über Datei und Zeile, sodass zwei Constraints mit zufällig gleicher ID nie zu einer kollabieren. Das Ergebnis: 348.
Jede einzelne beweisen
Für jede Occurrence muss unser Build genau eines von drei Urteilen halten, erzwungen von einem Generator, der das Verzeichnis bei jedem Lauf neu berechnet:
- Nachweislich durchgesetzt (303): Ein occurrence-exakter Test treibt den echten Evaluator über ein minimales Dokument und stellt sicher, dass diese Datei-und-Zeile-Occurrence evaluiert wurde. Der AST des Tests wird maschinell verifiziert – das Subject muss genau einmal ausgeführt werden, sein Ergebnis muss in genau eine Assertion fließen – und die Testdatei samt ihrer transitiven Importe ist hash-gepinnt. Wer den Evaluator ändert, entwertet die Evidenz, bis sie neu abgeleitet ist.
- Schema-durchgesetzt (1): nachweislich vom Release-JSON-Schema abgedeckt.
- Ausgeschlossen mit Begründung (44): explizit draußen, jede mit schriftlichem, review-fähigem Grund.
Unerklärt: null.
Der letzte Bucket – die Ausschlüsse – ist der, in dem es interessant wurde.
Der Standard hat Bugs
Wer 348 Constraints einzeln prüft, findet Dinge. Drei unserer Ausschlüsse sind Defekte in den NIST-Quellen selbst:
- Eine verwaiste Constraint.
oscal_mapping-common_metaschema.xml, Zeile 657, beschränkt ein Flag, das fünf Zeilen weiter oben definiert wird – und dann von nichts referenziert wird. Kein existierbares Dokument trägt dieses Flag. Die Constraint ist unerfüllbar. - Ein unmögliches Prädikat. Eine Regel für
inventory-item-Prop-Namen greift nur, wenn@typegleichsoftware,hardwareoderserviceist – aberinventory-itemdeklariert überhaupt keintype-Flag. (Sein Nachbarasset-idist in der Quelle selbst auskommentiert.) Das Prädikat kann nie matchen. - Constraints in Kommentaren. Fünf Constraint-Occurrences leben in Kommentarblöcken – darunter ein komplettes Wertevokabular für
with-child-controlsim Profile-Metaschema. Ein Mensch, der die Quelle liest, sieht sie; das kompilierte Schema setzt sie nie durch. Alle drei Funde sind upstream gemeldet: usnistgov/OSCAL#2254, #2255, #2256.
Und das Audit schnitt in beide Richtungen: Unser eigener Evaluator hatte einen Bug, durch den Constraints auf Flags (wie die erlaubten Werte von action/@type) zwar ins Inventar kompilierten, aber nie feuerten. Wir haben ihn nicht durch Glück gefunden – das Closure-Gate verweigerte die Evidenz für genau diese zwei Occurrences. Exakt der Fehlermodus, für dessen Aufdeckung das ganze System gebaut ist.
Dann haben wir den Vergleich wirklich ausgeführt
„Die Referenzimplementierung übersieht Dinge" allein aus Quellen-Inspektion zu behaupten, ist genau die Art unbewiesener Aussage, gegen die dieses Projekt existiert. Also haben wir das Java OSCAL CLI 3.2.0 von Maven Central geladen, hash-verifiziert und beide Validatoren über eine Fixture-Familie gefahren, deren Basisdokumente auf beiden Seiten sauber durchlaufen – jede Abweichung ist damit auf genau eine injizierte Änderung zurückführbar.
OSCAL 1.2.2 hat jenseits der Versionsnummern genau zwei Constraints geändert (beide im SSP-Modell). Das Java CLI – mit seinen 1.2.1-Bindings – liegt bei jeder beobachtbaren Konsequenz falsch:
- Ein SSP mit hängendem
rel="validation"-Komponenten-Link verletzt 1.2.2. Java: valid, Exit 0. - Ein SSP mit hängendem
rel="validated-by"-Link ist laut 1.2.2 in Ordnung (die Constraint wurde gelöscht). Java: invalid. - Eine
responsible-roleohne das optionaleparty-uuidist laut 1.2.2 in Ordnung – NIST hat das Prädikat genau dafür ergänzt. Java meldet, wörtlich,Key reference [null] not found.
Und dann eines, das gar nichts mit Versionsversatz zu tun hat: ein Katalog, dessen Metadaten-action den Wert "type": "invented-type" deklariert – verboten auf Zeile 877 des Metadata-Metaschemas, in 1.2.2 wie in den 1.2.1-Quellen, die Java einbettet. Java erklärt ihn für valide, in JSON und XML. Die Constraint zielt auf ein Flag; still nicht feuernde Flag-Constraints sind exakt die Fehlerklasse, die unser Vollständigkeits-Gate in unserem eigenen Evaluator gefangen hat. Die Referenzimplementierung hat dieselbe Fehlerklasse – und kein Closure-Gate, das sie fängt. Wir haben sie upstream als metaschema-framework/oscal-cli#279 gemeldet. (Wir haben die Klasse mechanisch kartiert: genau 2 von 200 allowed-values-Occurrences zielen auf Flags, und das einzige fehlerfähige Mitglied ist das live getestete. Kein ungetesteter Rest.)
Und der Ehrlichkeits-Beat, denn der Lauf schnitt in beide Richtungen: Unser Validator lehnte NISTs eigenen SP-800-53-rev5-Katalog ab. Zwanzig Fehler, ein einziger Defekt – unser Index-Evaluator behandelte ein fehlendes optionales Key-Feld als Violation, wo die Metaschema-Spezifikation einen Null-Key vorsieht. Java akzeptierte die Datei; wir lagen falsch; wir haben es am selben Tag behoben, und 800-53 validiert sauber. Später fing der Lauf noch einen zweiten: Wir erzwangen das OSCAL-Action-Type-Vokabular auch dann, wenn action/@system eine eigene URI deklarierte – genau die organisationsweise Segmentierung, die NISTs eigene Bemerkungen vorsehen. Ebenfalls behoben. Eine Differential-Harness, die immer nur die Bugs der Gegenseite findet, ist keine Harness, sondern Marketing.
(Ebenfalls unter „beide Seiten" abgelegt: Keiner der beiden Resolver validierte sein eigenes Ergebnis semantisch. Javas resolve-profile schreibt anstandslos einen Katalog, den Javas eigenes validate anschließend ablehnt – und unserer tat dasselbe, schema-geprüft, aber nicht semantik-geprüft. Unserer verweigert semantisch invalide Auflösungen inzwischen; Java kann das weiterhin nicht, denn dafür braucht man eine semantische Schicht, der man traut.)
Den Graphen validieren, nicht nur die Datei
Das Zweite, was der bestehenden Werkzeuglandschaft fehlt: OSCAL-Dokumente leben nicht allein. Ein Assessment Result importiert einen Assessment Plan, der ein SSP importiert, das ein Profil importiert, das gegen Kataloge aufgelöst wird. Die Modelldokumentation ist voller Beziehungen, die kein Einzeldatei-Validator prüfen kann – „das Target dieses Findings muss auf ein Statement der Baseline auflösen", „dieses POA&M muss sein System identifizieren".
Also haben wir dokumentübergreifende Validierung gebaut, die diese Kanten wirklich auflöst. Wer ein SSP prüfen lässt, bekommt die importierte Baseline durch die vollständige (Draft-)Profilauflösungspipeline bis zu den Katalogen aufgelöst, die selektierte Control-Menge berechnet – und Antworten wie diese:
{
"ruleId": "SSP-003.b",
"severity": "warning",
"path": "/…/implemented-requirements/1/control-id",
"message": "implemented control 'ac-99' is not supplied by the resolved import-profile 'file:///…/baseline.json'"
}
Mit einer entscheidenden Designregel: Diese Beziehungsprüfungen sind Interpretationen dokumentierter Prosa, und so behandeln wir sie – standardmäßig aus, Warnungen statt Fehler, der Auflösungs-Scope jedes Feldes an seine Quellzeile gepinnt, und alles bloß Erschließbare namentlich ausgeschlossen. Wo der Standard sich bedeckt hält, sollte ein Validator nicht bluffen. (Unser Lieblingsbeispiel: Die POA&M-Spezifikation sagt, ein SSP-Import oder eine System-ID sei erforderlich – „Both may be present." Wir warnen also nur, wenn keines von beiden existiert. Kein erfundenes Exklusiv-Oder.)
Warum TypeScript?
Weil die Dokumente in Browsern, Serverless-Funktionen, CLIs und Datenbanken leben – und wir einen Validator in allen wollten, nicht eine JVM-Sidecar daneben. Die Validatoren sind vorkompilierte Ajv-Standalone-Module: schlichtes generiertes JavaScript, null Schema-Kompilierung zur Laufzeit, byte-stabile Ausgabe. Derselbe Code, der unsere Validierungsoberflächen antreibt, läuft unverändert im Browser-Tab und in unserem CLI. Und Determinismus ist keine Ops-Nettigkeit – er ist es, der hash-gepinnte Evidenz überhaupt möglich macht.
import { createDiagnosticsOscalProcessor } from "@secani/oscal/diagnostics";
const result = createDiagnosticsOscalProcessor({ maxIssues: 50 }).validate(doc);
Wo das landet
- Volle OSCAL-Unterstützung 1.1.2 → 1.2.2, 1.2.2-native Semantik
- Vollständige Profilauflösung nach der Draft-Spezifikation, mit Fail-closed-Verhalten für alles, was der Draft offenlässt – und einem Resolver, der semantisch invalides Output verweigert
- Die gesamte OSCAL-Oberfläche des Java CLI, plus die Graph-Validierung, die es nie hatte (
validate --resolve-imports --interpretive all) - Und eine Behauptung, die man auditieren kann: 348/348 Constraint-Occurrences erklärt, in einem maschinenlesbaren Verzeichnis, das unsere CI bei jedem Commit neu berechnet
Der vollständige technische Report – Autoritätsmodell, Vollständigkeits-Closure, die Differential-Belegtabelle und was wir bewusst nicht behaupten – steht in der Validator-Dokumentation. Der Validator ist in privater Vorbereitung vor einem Open-Source-Release; die gehostete Validierungs-API exponiert heute die Schema-Ebene, die semantische Schicht folgt.
Compliance-Workflows prüfbar aufbauen
Secani verbindet Scopes, Nachweise, Aufgaben und KI-Agenten in einem gemeinsamen Arbeitskontext.
Weiterführende Beiträge
Alle BeiträgeOSCAL macht aus Compliance-Dokumenten strukturierte Daten: acht Dokumentmodelle, drei Formate und ein Ökosystem, das gerade zum Standard für Regulierung wird.
Mit RFC-0024 und den Consolidated Rules 2026 macht FedRAMP strukturierte Autorisierungsdaten zur Pflicht. Die Fristen sind gestaffelt – die Richtung ist eindeutig.
Das OSCAL-Ökosystem wächst schnell: Viewer und Validatoren sind gut abgedeckt, beim Autorieren und in Arbeitsabläufen bleibt die größte Lücke.