SecaniDokumentation
OSCAL

Validator

Wie der Secani-OSCAL-Validator quellenverifizierte Validierung jenseits der Java-Referenzimplementierung belegt – Vollständigkeits-Closure, Differential-Belege und ehrliche Grenzen.

Private Vorbereitung: Der hier beschriebene Validator ist Teil des secani/oscal-Toolkits, das während der Open-Source-Vorbereitung privat bleibt. Alle Zahlen, Zitate und Quellen-Anker stammen aus der maschinenlesbaren Registry im validation/-Verzeichnis des Toolkits und sind über pnpm verify reproduzierbar.

Zusammenfassung

Secanis OSCAL-Toolkit validiert NIST-OSCAL-Dokumente gegen das, was die gepinnten NIST-Quellen tatsächlich festlegen – nicht gegen das, was eine Referenzimplementierung zufällig tut. Es erreicht volle Fähigkeits-Parität mit der Oberfläche des Java OSCAL CLI 3.2.0 und geht in vier Punkten darüber hinaus, die der Java-Stack nicht versucht:

  1. Ein maschinell geprüfter Vollständigkeitsbeweis: Jede einzelne der 348 Constraint-Occurrences, die in den acht OSCAL-1.2.2-Root-Metaschemas auffindbar sind, ist individuell abgeglichen – nachweislich durchgesetzt, nachweislich schema-abgedeckt oder explizit mit schriftlicher Begründung ausgeschlossen. Null sind unerklärt.
  2. Native OSCAL-1.2.2-Semantik mit Multi-Release-Unterstützung (1.1.2, 1.1.3, 1.2.1, 1.2.2), während Java CLI 3.2.0 OSCAL-1.2.1-Bindings einbettet und 1.2.2-Dokumente nur auf Kompatibilitätsbasis verarbeiten kann.
  3. Dokumentübergreifende Workflow-Validierung – Auflösen und Prüfen des Referenzgraphen über SSPs, Assessment Plans, Assessment Results und POA&Ms – was vollständig außerhalb der Validierungsoberfläche des Java CLI liegt.
  4. Eine TypeScript-Codebasis, die überall läuft, wo die Dokumente leben: Dieselben vorkompilierten Validatoren laufen im Browser, in Serverless-API-Routen, im CLI und im Backend – nirgends eine JVM.

Der Prozess hat außerdem Defekte in den NIST-Quellen selbst zutage gefördert (siehe Bugs finden – in beide Richtungen) – genau das, was man von Occurrence-Level-Verifikation erwarten würde, und ihr stärkster Beleg.

Das Autoritätsmodell: Quellen vor Implementierungen

Die bindende Scope-Regel des Projekts:

Die NIST-OSCAL-Quelle ist die Autorität. Java ist ein Kompatibilitäts-Komparator und Implementierungsbeleg, niemals die Autorität.

Konkret ist jede semantische Aussage auf Bytes gepinnt:

  • Autorität: usnistgov/OSCAL beim Tag v1.2.2, Commit 21403b4ad2f162ef1201e5ee70e8b93f254f51ac – die acht Root-Metaschema-Module (Catalog, Profile, Component-Definition, SSP, Assessment-Plan, Assessment-Results, POA&M, Mapping) plus ihre gemeinsamen Importe (Metadata, Control-Common, Implementation-Common, Assessment-Common, Mapping-Common) und externe Constraint-Entities. Alle sind byte-exakt mit SHA-256-Pins gecacht.
  • Profilauflösung: die NIST-Draft-Spezifikation src/specifications/profile-resolution/profile-resolution-specml.xml. Weil sie Draft/WIP ist, sind ihre Anforderungen hinter einer expliziten Capability implementiert und werden nie stillschweigend in die gewöhnliche Validierung eingefaltet.
  • Komparator: Java OSCAL CLI 3.2.0 mit liboscal-java 7.2.0, das OSCAL-1.2.1-Bindings einbettet. Komparator-Belege werden mit occurrence-exakten Lokatoren aufgezeichnet; Referenzen ohne gecachte Bytes und exakte Lokatoren gelten als unqualifizierte Quellen-Inspektion und können kein Provenance-Gate schließen.

Das ist die entscheidende Umkehrung: Die meisten Werkzeuge behandeln die Referenzimplementierung als Ground Truth. Wir behandeln die Quellen des Standards als Ground Truth – und die Referenzimplementierung als Zeugen im Kreuzverhör.

Beispiel – wie eine lokator-gepinnte Aussage aussieht. Die Registry sagt nicht „wir unterstützen Responsible-Party-Rollenprüfungen". Sie sagt: Constraint oscal-metadata-responsible-party-role-ids, Quelle oscal_metadata_metaschema.xml@L400, SHA-256 der Quelldatei gepinnt, durchgesetzt von Regel META-002.a, bewiesen durch einen benannten Test, dessen AST-Hash aufgezeichnet ist. Ändert NIST diese Zeile, bricht der Pin und die Aussage muss requalifiziert werden.

Die Vollständigkeits-Closure: jede Constraint erklärt

Die Metaschema-Quellen definieren Constraints (allowed-values, is-unique, matches, index, index-has-key, has-cardinality, expect) verstreut über tausende Zeilen XML. Ein Validator kann behaupten, „OSCAL-Constraints zu unterstützen", ohne dass jemand prüfen kann, was das bedeutet. Wir haben es prüfbar gemacht.

Die Vollständigkeits-Closure inventarisiert lexikalisch 348 Constraint-Occurrences über den gepinnten Modulgraphen – einschließlich 17 anonymer Occurrences und 8 in Quellkommentaren erhaltener Occurrences, denn die Entscheidung, dass sie nicht zählen, ist selbst eine review-pflichtige Entscheidung. Die Occurrence-Identität umfasst Quellpfad und Zeile, sodass wiederholte Constraint-IDs nie kollabieren.

Jede Occurrence muss in genau einem geschlossenen Evidenz-Bucket landen:

BucketAnzahlBedeutung
assertion303Zur Laufzeit nachweislich durchgesetzt, per occurrence-exaktem Test
schema-enforced1Nachweislich vom Release-JSON-Schema abgedeckt
excluded-with-rationale44Explizit ausgeschlossen, jeweils mit konkretem Grund
unresolved0

Beispiel – was „occurrence-exakte Evidenz" im Code bedeutet. Jede bewiesene Occurrence hat eine Deklaration wie diese in der Testsuite:

closureEvidenceTest({
  assertionIds: ["META-012.b"],
  name: "allowed-values assessment-common oscal-assessment-objective-types@L65 occurrence",
  releases: [],
  subject: "evaluateBuiltinAllowedValues",
}, () => {
  const evaluation = evaluateBuiltinAllowedValues({
    "assessment-results": {
      "local-definitions": {
        "objectives-and-methods": [
          { "parts": [{ "name": "assessment" }] }
        ]
      }
    },
  }, "assessment-results");
  expect(evaluation).toMatchObject({
    evaluatedInventoryIds: expect.arrayContaining([
      "oscal_assessment-common_metaschema.xml#oscal-assessment-objective-types@L65",
    ]),
  });
});

Der Test treibt den echten Evaluator über ein minimales Dokument und stellt sicher, dass genau diese Quell-Occurrence – Datei, Constraint-ID, Zeile – evaluiert wurde. Das Evidenzformat ist bewusst schwer zu fälschen: Der TypeScript-AST der Deklaration wird verifiziert (das deklarierte Subject muss genau einmal ausgeführt werden; sein Ergebnis muss in genau ein Top-Level-expect fließen), und die Deklaration, ihre Datei und die SHA-256 ihrer transitiven Import-Closure sind gepinnt. Wer den Evaluator ändert, invalidiert jeden abhängigen Evidenz-Hash, bis er mechanisch neu abgeleitet wird. Ein umbenannter Test, ein No-Op-Body oder ein mutierter Helper entwertet die Evidenz automatisch.

Die 44 Ausschlüsse sind kein Weichzeichnen: 5 Occurrences stehen in XML-Kommentaren der NIST-Quellen (toter Text), 2 sind Upstream-Defekte (siehe unten), 3 sind dokumentierte Modellgrenzen, und 34 sind aktive Constraints, die zur Laufzeit standardmäßig durchgesetzt werden, aber außerhalb des gesperrten 93-Assertion-Inventars der Research-Registry liegen – jeder Ausschluss benennt den generierten Evaluator, der sie durchsetzt.

Mit unresolved = 0 schaltet das Artefakt exhaustiveRequirementClaimAllowed: true – ein Flag, das der Generator aus den Bucket-Zahlen berechnet, kein Satz, den ein Mensch schreibt.

Anforderungs-Registry: 40 von 42, mit ehrlichen Zurückstellungen

Über der Occurrence-Ebene liegt eine Registry von 42 Research-Anforderungen (93 atomare Assertions) über Schema-Routing, Metadaten-Semantik, Catalog-/Profile-Regeln, Profilauflösung, dokumentübergreifende Auflösung und Workflow-Validierung. 40 sind implemented; jeder Statuswechsel erforderte einen benannten Laufzeitpfad plus echte Positiv-/Negativtests. Die zwei offenen Punkte sind bewusste, dokumentierte Zurückstellungen:

  • PRES-004 (Import-Identifier-Mappings): Die Draft-Spezifikation nennt fünf optionale Mapping-Teilbereiche, definiert aber nur einen konkret (mapping[].controls[{from,to}]), und das gepinnte 1.2.2-Profile-Metaschema definiert überhaupt kein mapping auf import. Wir implementieren die beispielgestützte Form und schlagen bei allem anderen kontrolliert fehl:

    $ oscal-cli resolve-profile --to JSON profile-with-param-mapping.json
    oscal-cli: unsupported import mapping subsection 'params' in pinned draft

    Volle Mapping-Unterstützung zu behaupten hieße, Semantik zu erfinden, die die Quelle nicht enthält.

  • META-013 (äquivalente alternative Ressourcen-Repräsentationen): Die Quelle verlangt, dass mehrere rlink-/base64-Repräsentationen einer Ressource „äquivalente Informationen enthalten", ohne Äquivalenz operational zu definieren (Byte-Gleichheit? semantische Gleichheit nach Formatkonvertierung?). Die Durchsetzung bleibt ausgesetzt, bis eine Definition existiert – statt eine zu erfinden.

Was der Validator kann, was Java CLI 3.2.0 nicht kann

Native 1.2.2-Semantik, Multi-Release-Routing

Java CLI 3.2.0 bettet liboscal-java 7.2.0 mit OSCAL-1.2.1-Bindings ein; ein unveränderter Lauf über ein 1.2.2-Dokument ist beobachtete Kompatibilität, keine 1.2.2-Validierung. Secani liefert versionsbewusstes Schema-Routing und vorkompilierte Validatoren für 1.1.2, 1.1.3, 1.2.1 und 1.2.2, mit 1.2.2 als semantischem Implementierungsziel – und schlägt bei nicht qualifizierten Releases kontrolliert fehl, statt still gegen die falsche Generation zu validieren.

Die generierte semantische Constraint-Schicht

Die gewöhnliche (standardmäßig aktive) semantische Schicht evaluiert pro Dokument das gepinnte Constraint-Inventar: alle 200 aktiven allowed-values-Occurrences (einschließlich Descendant-Achsen-Targets und flag-adressierter Targets wie action/@type), alle 27 gescopten is-unique-Occurrences, 24 von 25 aktiven matches-Occurrences (die 25. wird von einer übergeordneten Metadatenregel durchgesetzt), die generierten expect-, has-cardinality- und datei-lokalen index-/index-has-key-Evaluatoren sowie die semantischen Regelfamilien für Metadata/Catalog/Profile/SSP. Jede Diagnose trägt ihre NIST-Provenienz. Dies ist die wörtliche Laufzeitausgabe für einen Katalog, dessen Metadaten-action den Wert "type": "invented-type" deklariert:

{
  "ruleId": "oscal-metadata-action-type-values",
  "sourceId": "https://raw.githubusercontent.com/usnistgov/OSCAL/21403b4ad2f162ef1201e5ee70e8b93f254f51ac/src/metaschema/oscal_metadata_metaschema.xml#L877",
  "sourceType": "oscal-constraint",
  "authority": "nist-metaschema",
  "severity": "error",
  "phase": "semantic",
  "path": "/catalog/metadata/actions/0/type",
  "message": "value \"invented-type\" is not one of the allowed values: \"approval\", \"request-changes\"",
  "keyword": "allowed-values"
}

Die sourceId ist kein Label – sie ist ein klickbarer, commit-gepinnter Verweis auf exakt die Metaschema-Zeile, die die Regel vorschreibt. (Diese Diagnose ist zugleich ein Live-Differential: Java CLI 3.2.0 akzeptiert genau dieses Dokument – in JSON und XML –, obwohl die Constraint auf Zeile 877 des 1.2.1-Metadata-Metaschemas steht, das seine eigenen Bindings einbetten; siehe Beleg R4 unten.)

Vollständige Profilauflösung nach der Draft-Spezifikation

resolve-profile implementiert die NIST-Draft-Auflösungspipeline Ende zu Ende – und schlägt kontrolliert fehl, wo der Draft undefiniert ist, statt zu raten:

$ oscal-cli resolve-profile --to JSON baseline-profile.json resolved-catalog.json
$ oscal-cli resolve-profile --to JSON legacy-combine.json
oscal-cli: deprecated combine method 'merge' has undefined resolution behavior

$ oscal-cli resolve-profile --to JSON mixed-major.json
oscal-cli: major OSCAL version mismatch between '1.2.2' and '2.0.0' at 'file:///…/legacy.json'

Abgedeckt: Import-Beschaffung mit Zyklus-/Tiefen-/Byte-Budgets, interne #uuid-Importe einschließlich base64-eingebetteter Dokumente, Include-/Exclude-Selektion mit with-child-controls-Expansion, keep-/use-first-Combine-Semantik, as-is-/custom-/flache Strukturierung, deterministische Parameter- und Alteration-Anwendung sowie Back-Matter-Merge mit den exakten Ordnungsregeln des Drafts (spätere Duplikate ersetzen an der späteren Position; eine keep=always-Ressource blockiert unmarkierte Ersetzung).

In der Output-Finalisierung stecken mehrere subtile Garantien: Die deklarierte oscal-version des Ergebnisses wird auf das höchste unterstützte Release derselben Major-Version geklemmt; Schlüssel werden in kanonischer OSCAL-Reihenfolge ausgegeben; und der aufgelöste Katalog wird vor der Serialisierung erneut validiert – Schema und semantische Constraints –, sodass ein invalides Auflösungsergebnis eine Verweigerung ist, kein Output. Das Java CLI führt diese Prüfung auf seinem eigenen Output nicht durch: Sein Resolver schreibt einen Katalog, den sein eigener Validator anschließend ablehnt (Beleg R6 unten).

Dokumentübergreifende Workflow-Validierung (nicht in der Java-Oberfläche)

Das ist der Differenzierer. OSCAL-Compliance-Artefakte bilden einen Graphen – ein Assessment Result importiert einen Assessment Plan, der ein SSP importiert, das ein Profil importiert, das gegen Kataloge aufgelöst wird – und die Modelldokumentation beschreibt Beziehungen, die kein JSON-Schema prüfen kann. Der Dokumentgraph-Validator löst diese Kanten auf und verifiziert sie.

Beispiel. Ein SSP behauptet, Control ac-99 zu implementieren, aber die importierte Baseline liefert dieses Control nie. Mit aktivierten interpretativen Regeln löst der Graph-Validator die Baseline durch die vollständige Profilauflösungspipeline bis zu den Katalogen auf, berechnet die selektierte Control-Menge und emittiert (wörtliche Issue-Form):

{
  "ruleId": "SSP-003.b",
  "sourceId": "secani:oscal-ssp-import-profile-interpretation:v1",
  "sourceType": "validation-dispatch",
  "authority": "secani-policy",
  "severity": "warning",
  "phase": "resolution",
  "path": "/system-security-plan/control-implementation/implemented-requirements/1/control-id",
  "message": "implemented control 'ac-99' is not supplied by the resolved import-profile 'file:///…/baseline.json'",
  "keyword": "implemented-control-supplied"
}

Man beachte die Provenienz-Disziplin auch hier: Die Autorität ist secani-policy unter einer versionierten Interpretationsquelle – nicht nist-metaschema –, weil diese Prüfung unsere dokumentierte Interpretation ist, nicht NISTs Maschinen-Constraint. Im CLI sind die Prüfungen über oscal-cli validate --resolve-imports --interpretive SSP-003.b ssp.json erreichbar (wiederholbar, oder --interpretive all; --warnings-as-errors macht daraus einen fehlschlagenden Exit).

Die vollständige Regelfamilie, jeweils an ihren Anker in den Quellen gepinnt:

RegelWas sie prüftAnker
SSP-003Jede SSP-implemented-requirement/control-id wird vom aufgelösten import-profile geliefertaufgelöst über die Profilauflösungspipeline
XDOC-001AP import-ssp → ein SSP; AR import-ap → ein AP; POA&M import-ssp → ein SSP; Release-GatingImport-Felder der AP-/AR-/POA&M-Metaschemas
ASMT-002related-observation-/associated-risk-/related-finding-UUIDs lösen in ihrem dokumentierten lokalen Scope aufoscal_assessment-common_metaschema.xml @L863/@L877; oscal_poam_metaschema.xml @L135
ASMT-003Assessment-Tool-Komponentenreferenzen lösen gegen Assessment-Assets auf, einschließlich AR→AP-Asset-Sichtbarkeitorigin-actor @L1025-1039
ASMT-001Finding-Targets lösen durch die AR→AP→SSP→Profil→Katalog-Kette auf, mit target/@type-Übereinstimmungfinding-target @L735-755 + Katalog-Part-Vokabular @L316-341
POAM-001Das dokumentierte System-Kontext-Prädikat – exakt wie geschriebensiehe unten

Beispiel – Prosa exakt implementieren. Die POA&M-Metaschema-Bemerkung lautet:

"Either an OSCAL-based SSP must be imported, or a unique system-id must be specified. Both may be present." – oscal_poam_metaschema.xml @L58-60

POAM-001.a warnt nur, wenn keines von beiden vorhanden ist. Ein Test pinnt, dass beides zusammen still bleibt – denn ein Exklusiv-Oder zu erfinden, das die Quelle nicht formuliert, ist genau die Art Über-Validierung, die dieses Projekt verweigert.

Zwei Designentscheidungen wiegen so schwer wie die Prüfungen selbst:

  • Interpretative Ehrlichkeit. Diese Beziehungen sind in Prosa dokumentiert, nicht als Maschinen-Constraints – bezeichnenderweise existiert NISTs eigener index-has-key für diese UUIDs im Assessment Plan nur als auskommentiertes „bogus example". Die Regeln sind daher standardmäßig aus, emittieren Warnungen, kippen ein Dokument nie auf invalid und laufen nur, wenn ein Aufrufer sie explizit benennt.
  • Scope vor Code gepinnt. Die Registry-Notiz jeder Regel dokumentiert pro Referenzfeld den dokumentierten Auflösungs-Scope samt Lokator – und was namentlich ausgeschlossen wurde. subject-uuid etwa ist dokumentiert als auflösend über „every file imported directly or indirectly" (@L652-654) – transitiv dokumentübergreifend – und daher explizit außerhalb des Scopes statt halb implementiert.

CLI-Oberfläche: Parität plus

Alle 22 OSCAL-Fähigkeiten des Java CLI sind mit beobachtbaren Akzeptanzverträgen abgedeckt (Kommando, Optionen, Input, Erfolgs-/Fehlerausgabe, Exit-Codes): validate/convert über JSON/XML/YAML, resolve-profile, list-allowed-values, deterministisches SARIF (--sarif-include-pass, --sarif-timing), --threads-Worker-Partitionierung mit kanonischer Ergebnisreihenfolge, metapath-/xpath-/jsonpointer-Pfaddarstellung, Modell-Kommando-Aliasse, Shell-Completion. Obendrauf: --prune, --interpretive, versionsgepinnte Schema-Exporte und die programmatische Diagnostics-API.

Warum TypeScript

  1. Ein Artefakt, jede Laufzeitumgebung. Die Validatoren sind Ajv-Standalone-vorkompilierte Module – schlichtes generiertes JavaScript, keine Schema-Kompilierung zur Laufzeit, keine Reflexion, kein Metaschema-Interpreter. Der identische Codepfad läuft im Browser, in Serverless-API-Routen, in Node (CLI) und im Backend. Der Java-Stack bräuchte an jedem dieser Orte eine JVM – oder einen verlustbehafteten Port.

  2. Die Plattform ist da, wo die Dokumente leben. Secanis Produkt ist eine Web-Plattform; ihre Validierungs-API, die Authoring-Workbench und der MCP-Server konsumieren das Toolkit als in-process, typisierte Bibliothek:

    import { createDiagnosticsOscalProcessor } from "@secani/oscal/diagnostics";
    
    const processor = createDiagnosticsOscalProcessor({ maxIssues: 50 });
    const result = processor.validate(document);
    // OscalValidationResult:
    // { ok, complete, modelType, oscalVersion, layers,
    //   errors, issues, totalIssueCount, truncated }

    Kein Subprozess-IPC gegen ein Java-Binary, kein Sidecar-Container, keine Cold-Start-JVM.

  3. Determinismus als Feature. Vorkompilierte Validatoren + generierte Evaluatoren + kanonische Schlüsselreihenfolge machen die Ausgaben byte-stabil – was dem Evidenzsystem überhaupt erst erlaubt, Hashes über Verhalten zu pinnen.

  4. Distribution. Ein typisiertes Paket mit dedizierten Entry-Points (., ./browser, ./versioned, ./diagnostics) erreicht das Ökosystem, in dem Compliance-UIs tatsächlich gebaut werden.

  5. Typsicherheit gegen das Modell. Generierte Modell-Maps machen „diese Constraint zielt auf ein Flag, nicht auf ein Element" zu einer statisch prüfbaren Eigenschaft – mit einem Guard-Test, der belegt, dass über alle 1.011 Knotenformen des gepinnten Modells keine Flag-/Element-Namenskollision existiert.

Was TypeScript uns nicht erkauft hat, ist Befreiung vom Beweis: Die Vollständigkeits-Closure existiert genau dafür, dass die Sprachwahl durch Occurrence-Level-Evidenz gedeckt ist, nicht durch Behauptungen über Developer Experience.

Bugs finden – in beide Richtungen

Occurrence-Level-Verifikation hat echte Defekte auf beiden Seiten gefunden – das stärkste Argument dafür, dass die Methodik funktioniert.

In unserem eigenen Evaluator (gefunden vom Closure-Gate). Zwei Metadaten-Constraints zielen auf Flags (Attribute) statt auf Kindelemente – die erlaubten Werte von action/@system und action/@type (oscal_metadata_metaschema.xml @L874, @L877). Sie kompilierten ins unterstützte Inventar, feuerten aber nie: Der Pfad-Walker des Evaluators löste Namensschritte nur gegen Modellelemente auf, sodass beide Constraints still ins Leere liefen. Das Closure-Gate verweigerte die Evidenz für sie – so kam der Bug ans Licht. Behoben mit korrekter Flag-Schritt-Auflösung; ein statischer Walk über den Definitionsgraphen bewies, dass genau diese zwei Occurrences betroffen waren.

In unserem eigenen Evaluator (gefunden vom Live-Differential-Lauf – beide am selben Tag behoben). Erstens behandelte der index-Evaluator einen fehlenden Key-Field-Wert als Fehler und lehnte damit jeden Katalog mit einem prop ohne uuid ab – einschließlich NISTs eigenem SP-800-53-rev5-Katalog (20 gemeldete Fehler; Java 3.2.0 akzeptiert die Datei). Die Metaschema-Spezifikation ist eindeutig: Ein fehlender Key-Field-Wert ergibt einen Null-Index-Key, keine Violation; 800-53 validiert jetzt sauber. Zweitens blieb das OSCAL-Action-Type-Vokabular auch dann bindend, wenn action/@system eine eigene URI deklarierte – im Widerspruch zu NISTs Bemerkung, @system biete „a means to segment the value space for the type" pro Organisation. Beide Fixes kamen mit Regressionstests und neu abgeleiteten Evidenz-Hashes.

In den NIST-Quellen:

  • Eine verwaiste Constraint. oscal_mapping-common_metaschema.xml @L657 beschränkt ein bei @L652 definiertes Flag – das von keinem flag ref referenziert wird. Kein Modellknoten trägt es; die Constraint ist für jedes existierbare Dokument unerfüllbar.
  • Ein unmögliches Prädikat. oscal_implementation-common_metaschema.xml @L567 beschränkt Prop-Namen unter einem @type-Prädikat auf inventory-item – aber inventory-item deklariert kein type-Flag (das benachbarte asset-id-Flag ist selbst bei @L456-461 auskommentiert). Das Prädikat kann nie matchen.
  • Constraints in Kommentaren. Fünf Occurrences – darunter ein with-child-controls-Wertevokabular im Profile-Metaschema – existieren nur innerhalb von Kommentarblöcken: Text, den ein lexikalischer Leser findet, den das kompilierte Schema aber nie durchsetzt.

Jeder Fall ist als excluded-with-rationale mit exaktem Lokator aufgezeichnet – auditierbar, und upstream gemeldet: usnistgov/OSCAL#2254 (verwaistes Flag), #2255 (unmögliches Prädikat), #2256 (Constraints in Kommentaren). Die Durchsetzungslücke im Referenz-Tooling ist als metaschema-framework/oscal-cli#279 gemeldet.

Der Live-Differential-Lauf: Belege, in beide Richtungen

Am 18.07.2026 haben wir die Lücke zwischen quellen-inspizierten Komparator-Aussagen und ausgeführter Evidenz geschlossen: Das Java OSCAL CLI 3.2.0 wurde von Maven Central geladen, byte-verifiziert gegen die registry-gepinnten SHA-256/SHA-512, und über eine handgebaute Fixture-Familie gefahren, deren Basisdokumente auf beiden Toolchains sauber validieren – jedes Differential ist damit auf genau eine injizierte Änderung zurückführbar. Sein eingebetteter OSCAL-Commit (26df0501) ist der 1.2.1-Patch-Release-Commit und bestätigt die Bindings-Lücke zur Laufzeit. Vollständige Transkripte, Fixtures und Exit-Codes sind als Differential-Belege neben der Registry aufgezeichnet.

BelegEinzelne Änderung ggü. BaselineLaut NIST-1.2.2-QuellenJava 3.2.0Secani
R1SSP-Komponenten-Link rel="validation", hängender #hrefinvalid (ssp L628)valid, Exit 0invalid, Exit 1
R2SSP-Komponenten-Link rel="validated-by", hängender #hrefvalid (Constraint in 1.2.2 entfernt)invalid, Exit 1valid, Exit 0
R3responsible-role ohne optionales party-uuidvalid (1.2.2 ergänzte [party-uuid], L736)invalid, Exit 1Key reference [null] not foundvalid, Exit 0
R4metadata/action/@type = "invented-type"invalid (L877 – in 1.2.1- und 1.2.2-Quellen)valid, Exit 0 (JSON und XML)invalid, Exit 1
R5SSP implementiert ac-99, fehlt in der aufgelösten Baselineinterpretative Prosavalid, Exit 0Opt-in-Warnung SSP-003.b
R6Profil-Alter fügt prop status="operational" ins aufgelöste Control einaufgelöstes Ergebnis invalid (L289)resolve Exit 0 – lehnt dann das eigene Output bei validate abverweigert das Schreiben des Outputs
R7NIST-SP-800-53-rev5-Katalog, unverändertvalidvalid, Exit 0war invalid – unser Bug, am selben Tag behoben; jetzt valid

Drei Muster, ehrlich getrennt: R1–R3 sind reine Stale-Bindings-Differentiale – 1.2.2 hat jenseits der Versionsnummern genau zwei Constraints geändert, beide im SSP-Modell, und Java liegt bei allen drei beobachtbaren Konsequenzen falsch (ein False Negative, zwei False Positives, davon einer als Fehler über die Abwesenheit eines optionalen Feldes). R4 ist eine Durchsetzungslücke unabhängig vom Versionsversatz: Der Constraint-Text ist byte-identisch in den Quellen, die Java einbettet – und die betroffene Klasse ist vollständig kartiert: Eine mechanische Enumeration über das gepinnte Inventar zeigt, dass genau 2 von 200 aktiven allowed-values-Occurrences auf Flags zielen; das einzige fehlerfähige Mitglied ist das live getestete – es gibt keinen ungetesteten Rest. R5 ist eine Fähigkeitslücke, kein Defekt – und unsere Prüfung bleibt eine Opt-in-Warnung, weil die Quelle Prosa ist, keine Maschinen-Constraint. R6 und R7 trafen zuerst uns und wurden am selben Tag behoben; sie bleiben in der Tabelle, denn eine Differential-Harness, die immer nur die Bugs der Gegenseite findet, ist keine Harness.

Was wir bewusst nicht behaupten

  • Die Profilauflösungs-Spezifikation ist Draft; PRES-Verhalten ist feature-gated, und NIST-Constraints der Stufe WARNING bleiben Warnungen.
  • Die Workflow-Regeln sind Interpretationen dokumentierter Beziehungen, als solche markiert, standardmäßig aus.
  • Registry-Komparator-Evidenz bleibt als quellen-inspiziert gekennzeichnet; der Live-Differential-Lauf ergänzt sie um ausgeführte, hash-gepinnte Belege, hebt aber die Evidenzstufe der Registry nicht stillschweigend an – die Einfaltung der Transkripte in das formale Qualifikations-Gate wird separat verfolgt.
  • 34 zur Laufzeit durchgesetzte Constraints warten auf einen Scope-Review, bevor sie erstklassige Registry-Assertions erhalten; ihre Laufzeit-Durchsetzung ist davon unberührt.
  • Die gehostete Validierungs-API exponiert derzeit die Schema-Ebene; die hier beschriebene semantische Constraint-Schicht kommt mit einem späteren API-Release.

Anhang: gepinnte Referenzen

  • OSCAL-Quellen: usnistgov/OSCAL @ 21403b4ad2f162ef1201e5ee70e8b93f254f51ac (v1.2.2) – Metaschemas, JSON-Schemas, Profilauflösungs-Draft-Spezifikation
  • Unterstützte OSCAL-Releases: 1.1.2, 1.1.3, 1.2.1, 1.2.2 (semantisches Ziel: 1.2.2)
  • Modelldokumentation: https://pages.nist.gov/OSCAL/
  • Komparator: Java OSCAL CLI 3.2.0 / liboscal-java 7.2.0 (OSCAL-1.2.1-Bindings)
  • Maschinenlesbare Akzeptanz: validation/requirements.json (42 Anforderungen / 93 Assertions), validation/capabilities.json (22 Capability-Verträge), validation/completeness-closure.json (348 Occurrences), validation/scope-lock.json
  • Verifikation: 1.000+ Tests; pnpm verify (Typecheck → Tests → 13 Generated-Artifact-Gates → Build → Paketprüfungen → Smoke)