
Varje OSCAL-validator påstår att den ”validerar OSCAL”. Nästan ingen av dem kan berätta vad den meningen betyder.
NIST:s OSCAL-modeller är skrivna i Metaschema – åtta rotmoduler plus gemensamma importer, tusentals rader XML som definierar inte bara dokumentens form utan också deras semantik: tillåtna värdevokabulärer, unikhetsregler, korsreferensindex, krav på kardinalitet. Ett JSON Schema fångar formen. Det är i semantiken som efterlevnadsdokument faktiskt blir fel – och där ”vi validerar OSCAL” i tysthet blir ”vi validerar en del av OSCAL, vi vet inte riktigt vilken”.
Vi ville veta exakt vilken del. Så vi byggde vår validator på samma sätt som man reviderar ett system: utgå från källorna, räkna upp allt och redovisa varenda post.
Det första beslutet var det viktiga: NIST:s källor är auktoriteten – inte referensimplementationen. Vi låste usnistgov/OSCAL vid commit 21403b4a… (v1.2.2), cachade varje Metaschema-byte exakt med SHA-256-hashar och behandlade det ärevördiga Java OSCAL CLI som det det faktiskt är: en jämförelsepart att korsförhöra, inte en sanning att kopiera. (Det bäddar till exempel in OSCAL 1.2.1-bindningar – det kan bearbeta 1.2.2-dokument, men det kan inte tala 1.2.2.)
Sedan inventerade vi lexikalt varje förekomst av en constraint i de låsta källorna. Inte constraint-typer – förekomster, identifierade med fil och rad, så att två constraints som råkar dela ett ID aldrig slås ihop till en. Antalet blev 348.
För varje förekomst måste vårt bygge ha exakt ett av tre utlåtanden, upprätthållet av en generator som räknar om förteckningen vid varje körning:
Oredovisade: noll.
Den sista kategorin – undantagen – var där det blev intressant.
När man kontrollerar 348 constraints en i taget hittar man saker. Tre av våra undantag är defekter i själva NIST-källorna:
oscal_mapping-common_metaschema.xml rad 657 begränsar en flagga som definieras fem rader tidigare – och som sedan aldrig refereras av någonting. Inget dokument som kan existera bär den flaggan. Constrainten kan inte uppfyllas.inventory-item gäller bara när @type är software, hardware eller service – men inventory-item deklarerar ingen type-flagga alls. (Grannen asset-id är själv bortkommenterad i källan.) Predikatet kan aldrig matcha.with-child-controls. En människa som läser källan ser dem; det kompilerade schemat tillämpar dem aldrig. Alla tre fynden är rapporterade uppströms: usnistgov/OSCAL#2254, #2255, #2256.Och granskningen skar åt båda hållen: vår egen utvärderare hade en bugg där constraints som riktar sig mot flaggor (som de tillåtna värdena för action/@type) kompilerades in i förteckningen men i tysthet aldrig utlöstes. Vi hittade den inte av en slump – slutningsgrinden vägrade godta belägg för just de två förekomsterna, vilket är precis det felsätt som hela systemet är utformat för att avslöja.
Att hävda att ”referensimplementationen missar saker” enbart utifrån källgranskning är precis den sortens obevisade påstående som det här projektet finns till för att döda. Så vi laddade ned Java OSCAL CLI 3.2.0 från Maven Central, verifierade dess hash och körde båda validatorerna över en familj av testfixturer vars grunddokument passerar utan anmärkning på båda sidor – så att varje oenighet kan härledas till en enda injicerad ändring.
OSCAL 1.2.2 ändrade exakt två constraints utöver versionsuppräkningar (båda i SSP-modellen). Java-CLI:t – som bäddar in 1.2.1-bindningar – hamnar på fel sida i varje observerbar konsekvens:
rel="validation" bryter mot 1.2.2. Java: giltig, exit 0.rel="validated-by" är okej enligt 1.2.2 (constrainten togs bort). Java: ogiltig.responsible-role utan det valfria party-uuid är okej enligt 1.2.2 – NIST lade till predikatet just för att rätta detta. Java ger fel med, bokstavligen, Key reference [null] not found.Sedan ett fall som inte alls handlar om versionsskillnader: en katalog vars metadata-action deklarerar "type": "invented-type" – förbjudet på rad 877 i metadata-Metaschemat både i 1.2.2 och i de 1.2.1-källor som Java bäddar in. Java förklarar det giltigt, i både JSON och XML. Constrainten riktar sig mot en flagga; att constraints riktade mot flaggor i tysthet inte utlöses är precis den buggklass som vår fullständighetsgrind fångade i vår egen utvärderare. Referensimplementationen har samma buggklass – och ingen slutningsgrind som fångar den. Vi rapporterade det uppströms som metaschema-framework/oscal-cli#279. (Vi kartlade klassen mekaniskt: exakt 2 av 200 förekomster av allowed-values riktar sig mot flaggor, och den enda medlemmen på felnivå är den vi testade skarpt. Ingen otestad rest.)
Och ärlighetens ögonblick, eftersom körningen skar åt båda hållen: vår validator underkände NIST:s egen SP 800-53 rev5-katalog. Tjugo fel, alla samma defekt – vår indexutvärderare behandlade ett saknat valfritt nyckelfält som en överträdelse där Metaschema-specifikationen säger att det är en null-nyckel. Java godtog filen; vi hade fel; vi rättade det samma dag, och 800-53 validerar nu utan anmärkning. Körningen fångade senare en andra: vi fortsatte att tillämpa OSCAL:s vokabulär för action-typer även när action/@system deklarerade en egen URI – exakt den segmentering per organisation som NIST:s egna anmärkningar efterfrågar. Även den rättad. En differentialrigg som bara hittar den andra sidans buggar är ingen rigg, det är marknadsföring.
(Också under rubriken ”båda sidor”: ingen av resolverarna validerade sin egen utdata semantiskt. Javas resolve-profile skriver glatt ut en katalog som Javas eget validate sedan underkänner – och vår gjorde detsamma, med schemagrind men utan semantikgrind. Vår vägrar nu att skriva semantiskt ogiltiga upplösningar; Java kan fortfarande inte det, eftersom det kräver ett semantiskt lager man litar på att täppa till det hålet.)
Det andra som saknas i befintliga verktyg: OSCAL-dokument lever inte ensamma. Ett bedömningsresultat importerar en bedömningsplan, som importerar en SSP, som importerar en profil, som löses upp mot kataloger. Modelldokumentationen är full av samband som ingen validator för enskilda filer kan kontrollera – ”den här iakttagelsens mål måste kunna lösas upp till ett påstående i baslinjen”, ”den här POA&M:en måste identifiera sitt system”.
Så vi byggde validering över dokument som löser upp dessa kanter på riktigt. Be den kontrollera en SSP, så löser den upp den importerade baslinjen genom hela pipelinen för profilupplösning (enligt utkastspecifikationen) ned till katalogerna, beräknar den valda kontrolluppsättningen och talar om:
{
"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'"
}
Med en avgörande designregel: dessa sambandskontroller är tolkningar av dokumenterad prosa, och vi behandlar dem så – avstängda som standard, varningar och inte fel, varje fälts upplösningsomfång låst till sin källrad och allt som bara är möjligt att härleda uttryckligen undantaget. Där standarden garderar sig ska en validator inte bluffa. (Vårt favoritexempel: POA&M-specifikationen säger att en SSP-import eller ett system-id krävs – ”Both may be present.” Så vi varnar bara när ingetdera finns. Inget påhittat exklusivt eller.)
Eftersom dokumenten lever i webbläsare, serverlösa funktioner, CLI:n och databaser – och vi ville ha en validator i alla dessa, inte en JVM-sidovagn bredvid var och en. Validatorerna är förkompilerade fristående Ajv-moduler: vanlig genererad JavaScript, ingen schemakompilering vid körning, bytestabil utdata. Samma kod som driver våra valideringsytor körs oförändrad i din webbläsarflik och i vårt CLI. Och determinism är inte bara en bekvämlighet för driften – det är det som över huvud taget gör hashlåsta belägg möjliga.
import { createDiagnosticsOscalProcessor } from "@secani/oscal/diagnostics";
const result = createDiagnosticsOscalProcessor({ maxIssues: 50 }).validate(doc);
validate --resolve-imports --interpretive all)Den fullständiga tekniska rapporten – auktoritetsmodell, fullständighetsslutning, tabellen med kvitton från differentialkörningen och vad vi medvetet inte påstår – finns i validatorns dokumentation. Validatorn finns i privat staging inför en release som öppen källkod; det driftade validerings-API:et exponerar schemanivån i dag, och det semantiska lagret följer.
Secani kopplar samman omfattningar, bevis, uppgifter och AI-agenter i en gemensam arbetsyta.
OLIR tillhandahåller mappningsinnehåll och styrning. OSCAL ger den maskinläsbara strukturen för att använda mappningarna i gapanalyser, återanvändning av underlag och arbetsflöden för konsekvensanalys vid förändringar.
OSCAL gör efterlevnadsdokument till strukturerade data: åtta dokumentmodeller, tre format och ett ekosystem som håller på att bli standard för regelverk.
Med RFC-0024 och Consolidated Rules 2026 gör FedRAMP strukturerade auktorisationsdata obligatoriska. Tidsfristerna är förskjutna – riktningen är entydig.