
Elke OSCAL-validator beweert ‘OSCAL te valideren’. Bijna geen enkele kan u vertellen wat die zin betekent.
De OSCAL-modellen van NIST zijn geschreven in Metaschema: acht rootmodules plus gedeelde imports, duizenden regels XML die niet alleen de vorm van een document definiëren, maar ook de semantiek: vocabulaires met toegestane waarden, uniciteitsregels, indexen voor kruisverwijzingen, kardinaliteitseisen. Een JSON Schema vangt de vorm af. De semantiek is waar compliancedocumenten werkelijk de mist in gaan, en waar ‘wij valideren OSCAL’ ongemerkt verandert in ‘wij valideren een deel van OSCAL, we weten niet zeker welk deel’.
Wij wilden zeker weten welk deel. Dus bouwden we onze validator zoals je een systeem zou auditen: begin bij de bronnen, som alles op en geef rekenschap van elk afzonderlijk item.
De eerste beslissing was de belangrijkste: de NIST-bronnen zijn de autoriteit, niet de referentie-implementatie. We pinden usnistgov/OSCAL op commit 21403b4a… (v1.2.2), sloegen elk Metaschema byte-exact op met SHA-256-hashes en behandelden de eerbiedwaardige Java OSCAL CLI als wat hij werkelijk is: een vergelijkingspunt om aan een kruisverhoor te onderwerpen, geen waarheid om te kopiëren. (Hij bevat bijvoorbeeld OSCAL 1.2.1-bindings: hij kan 1.2.2-documenten verwerken, maar hij spreekt geen 1.2.2.)
Daarna inventariseerden we lexicaal elk voorkomen van een constraint in de vastgelegde bronnen. Geen constraint-typen, maar voorkomens, geïdentificeerd op bestand en regel, zodat twee constraints die toevallig een ID delen nooit tot één samenvallen. De telling kwam uit op 348.
Voor elk voorkomen moet onze build precies één van drie oordelen hebben, afgedwongen door een generator die het grootboek bij elke run opnieuw berekent:
Niet verantwoord: nul.
Die laatste categorie, de uitsluitingen, werd pas echt interessant.
Wie 348 constraints een voor een controleert, vindt dingen. Drie van onze uitsluitingen zijn defecten in de NIST-bronnen zelf:
oscal_mapping-common_metaschema.xml legt op regel 657 een constraint op een flag die vijf regels eerder is gedefinieerd, en daarna door niets wordt gerefereerd. Geen enkel document dat kan bestaan, bevat deze flag. De constraint is onvervulbaar.inventory-item is alleen van toepassing wanneer @type gelijk is aan software, hardware of service, maar inventory-item declareert helemaal geen type-flag. (De buurman, asset-id, is in de bron zelf uitgecommentarieerd.) Het predicaat kan nooit matchen.with-child-controls. Een mens die de bron leest, ziet ze; het gecompileerde schema dwingt ze nooit af. Alle drie de bevindingen zijn upstream gemeld: usnistgov/OSCAL#2254, #2255, #2256.En de audit werkte in twee richtingen: onze eigen evaluator had een bug waarbij constraints die op flags waren gericht (zoals de toegestane waarden van action/@type) wel in de inventaris werden gecompileerd, maar stilletjes nooit afgingen. Dat vonden we niet bij toeval: de afsluitingspoort weigerde bewijs voor die twee voorkomens te accepteren, en dat is precies de faalwijze die het hele systeem zichtbaar moet maken.
Op basis van broninspectie alleen beweren dat ‘de referentie-implementatie dingen mist’, is precies het soort onbewezen bewering dat dit project wil uitbannen. Dus downloadden we de Java OSCAL CLI 3.2.0 van Maven Central, verifieerden de hash en lieten beide validators los op een familie fixtures waarvan de basisdocumenten aan beide kanten foutloos slagen, zodat elk verschil toe te schrijven is aan één ingevoegde wijziging.
OSCAL 1.2.2 wijzigde, afgezien van versieverhogingen, precies twee constraints (beide in het SSP-model). De Java-CLI, die 1.2.1-bindings bevat, zit bij elk waarneembaar gevolg aan de verkeerde kant:
rel="validation" schendt 1.2.2. Java: geldig, exitcode 0.rel="validated-by" is volgens 1.2.2 in orde (de constraint is verwijderd). Java: ongeldig.responsible-role zonder de optionele party-uuid is volgens 1.2.2 in orde; NIST voegde het predicaat juist toe om dit te verhelpen. Java geeft een fout met, letterlijk, Key reference [null] not found.Dan één die helemaal niets met versieverschillen te maken heeft: een catalog waarvan de metadata-action "type": "invented-type" declareert, wat op regel 877 van het metadata-Metaschema is verboden, zowel in 1.2.2 als in de 1.2.1-bronnen die Java bevat. Java verklaart het geldig, in JSON en in XML. De constraint is op een flag gericht; constraints op flags die stilletjes niet afgaan, is precies de bugklasse die onze volledigheidspoort in onze eigen evaluator ving. De referentie-implementatie heeft dezelfde bugklasse, en geen afsluitingspoort om haar te vangen. We hebben het upstream gemeld als metaschema-framework/oscal-cli#279. (We hebben de klasse mechanisch in kaart gebracht: precies 2 van de 200 voorkomens van allowed-values zijn op flags gericht, en het enige lid met foutniveau is het voorkomen dat we live hebben getest. Er blijft niets ongetest over.)
En dan het eerlijke deel, want de run werkte in twee richtingen: onze validator wees NIST’s eigen SP 800-53 rev5-catalog af. Twintig fouten, allemaal hetzelfde defect: onze index-evaluator behandelde een ontbrekend optioneel sleutelveld als een schending, terwijl de Metaschema-specificatie zegt dat het een null-sleutel is. Java accepteerde het bestand; wij zaten fout; we hebben het dezelfde dag verholpen, en 800-53 valideert nu foutloos. Later ving de run er nog een: we bleven het OSCAL-vocabulaire voor action-typen afdwingen, ook wanneer action/@system een eigen URI declareerde, precies de segmentatie per organisatie waar NIST in zijn eigen opmerkingen om vraagt. Ook verholpen. Een vergelijkingsharnas dat alleen ooit de bugs van de andere kant vindt, is geen harnas, maar marketing.
(Eveneens onder ‘beide kanten’: geen van beide resolvers valideerde de eigen uitvoer semantisch. Java’s resolve-profile schrijft zonder morren een catalog weg die Java’s eigen validate vervolgens afwijst, en de onze deed hetzelfde: wel op schema gecontroleerd, niet op semantiek. De onze weigert nu semantisch ongeldige resoluties weg te schrijven; Java kan dat nog steeds niet, omdat je voor het dichten van dat gat een semantische laag nodig hebt die je vertrouwt.)
Het tweede dat in bestaande tooling ontbreekt: OSCAL-documenten staan niet op zichzelf. Een assessment result importeert een assessment plan, dat een SSP importeert, dat een profile importeert, dat wordt geresolveerd tegen catalogs. De modeldocumentatie staat vol relaties die geen enkele validator voor één bestand kan controleren: ‘het target van deze finding moet resolveren naar een statement in de baseline’, ‘deze POA&M moet zijn systeem identificeren’.
Dus bouwden we validatie over documenten heen die deze verbindingen echt resolveert. Vraag haar een SSP te controleren, en ze resolveert de geïmporteerde baseline via de volledige pipeline voor profile-resolutie (volgens de conceptspecificatie) tot aan de catalogs, berekent de geselecteerde set controls en vertelt u:
{
"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'"
}
Met één cruciale ontwerpregel: deze relatiecontroles zijn interpretaties van gedocumenteerd proza, en zo behandelen we ze ook: standaard uitgeschakeld, waarschuwingen in plaats van fouten, de resolutiescope van elk veld vastgelegd op de bronregel, en alles wat slechts afleidbaar is, bij naam uitgesloten. Waar de standaard voorbehouden maakt, moet een validator niet bluffen. (Ons favoriete voorbeeld: de POA&M-specificatie zegt dat een SSP-import of een system-id vereist is: ‘Both may be present.’ Dus waarschuwen we alleen wanneer geen van beide bestaat. Geen verzonnen exclusieve of.)
Omdat de documenten leven in browsers, serverless functions, CLI’s en databases, en we één validator in al die omgevingen wilden, niet naast elke omgeving een JVM-sidecar. De validators zijn voorgecompileerde standalone Ajv-modules: gewoon gegenereerd JavaScript, geen schemacompilatie tijdens runtime, byte-stabiele uitvoer. Dezelfde code die onze validatie aandrijft, draait ongewijzigd in uw browsertabblad en in onze CLI. En determinisme is niet alleen een operationeel pluspunt: het is wat bewijs met hashvastlegging überhaupt mogelijk maakt.
import { createDiagnosticsOscalProcessor } from "@secani/oscal/diagnostics";
const result = createDiagnosticsOscalProcessor({ maxIssues: 50 }).validate(doc);
validate --resolve-imports --interpretive all)Het volledige technische rapport (autoriteitsmodel, volledigheidsafsluiting, de tabel met vergelijkingsbewijzen en wat we bewust niet beweren) staat in de validatordocumentatie. De validator staat in een privéstagingomgeving in afwachting van een open-sourcerelease; de gehoste validatie-API ontsluit vandaag het schemaniveau, en de semantische laag volgt.
Secani verbindt scopes, bewijs, taken en AI-agents in één gedeelde werkruimte.
OLIR levert mappinginhoud en governance. OSCAL levert de machineleesbare structuur om die mappings te gebruiken in gapanalyses, hergebruik van bewijs en workflows voor wijzigingsimpact.
OSCAL maakt van compliancedocumenten gestructureerde gegevens: acht documentmodellen, drie formaten en een ecosysteem dat uitgroeit tot de standaard voor regelgeving.
Met RFC-0024 en de Consolidated Rules 2026 maakt FedRAMP gestructureerde autorisatiegegevens verplicht. De deadlines zijn gespreid – de richting is ondubbelzinnig.