
Fiecare validator OSCAL pretinde că „validează OSCAL”. Aproape niciunul nu vă poate spune ce înseamnă această propoziție.
Modelele OSCAL ale NIST sunt scrise în Metaschema: opt module rădăcină plus importuri comune, mii de linii de XML care definesc nu doar forma documentelor, ci și semantica lor: vocabulare de valori permise, reguli de unicitate, indexuri de referințe încrucișate, cerințe de cardinalitate. Un JSON Schema prinde forma. Semantica este însă locul în care documentele de conformitate greșesc de fapt și în care „validăm OSCAL” devine discret „validăm o parte din OSCAL, nu suntem siguri care”.
Noi am vrut să fim siguri care. Așa că ne-am construit validatorul așa cum ați audita un sistem: pornind de la surse, enumerând totul și ținând socoteala fiecărui element.
Prima decizie a fost cea importantă: sursele NIST sunt autoritatea, nu implementarea de referință. Am fixat usnistgov/OSCAL la commitul 21403b4a… (v1.2.2), am păstrat în cache fiecare octet al Metaschema exact, cu hash-uri SHA-256, și am tratat veneratul Java OSCAL CLI drept ceea ce este de fapt: un termen de comparație pe care îl supunem unui contrainterogatoriu, nu un adevăr de copiat. (Printre altele, el încorporează legături pentru OSCAL 1.2.1: poate procesa documente 1.2.2, dar nu vorbește 1.2.2.)
Apoi am inventariat lexical fiecare apariție a unei constrângeri din sursele fixate. Nu tipurile de constrângeri, ci aparițiile, identificate prin fișier și linie, astfel încât două constrângeri care se întâmplă să aibă același ID să nu se contopească niciodată într-una singură. Totalul a fost de 348.
Pentru fiecare apariție, build-ul nostru trebuie să dețină exact unul dintre trei verdicte, impus de un generator care recalculează registrul la fiecare rulare:
Neacoperite: zero.
Această ultimă categorie, excluderile, este cea în care lucrurile au devenit interesante.
Când verificați 348 de constrângeri una câte una, găsiți lucruri. Trei dintre excluderile noastre sunt defecte ale surselor NIST înseși:
oscal_mapping-common_metaschema.xml, linia 657, constrânge un flag definit cu cinci linii mai sus și apoi nereferențiat de nimic. Niciun document posibil nu conține acest flag. Constrângerea nu poate fi satisfăcută.inventory-item se aplică doar când @type este software, hardware sau service, dar inventory-item nu declară deloc un flag type. (Vecinul său, asset-id, este el însuși comentat în sursă.) Predicatul nu se poate potrivi niciodată.with-child-controls din profil. Un om care citește sursa le vede; schema compilată nu le aplică niciodată. Toate cele trei constatări au fost raportate în amonte: usnistgov/OSCAL#2254, #2255, #2256.Iar auditul a tăiat în ambele direcții: propriul nostru evaluator avea un bug din cauza căruia constrângerile care vizau flag-uri (precum valorile permise ale action/@type) erau compilate în inventar, dar nu se declanșau niciodată, fără niciun semnal. Nu l-am găsit din noroc: poarta de închidere a refuzat să accepte dovezi pentru aceste două apariții, care este exact modul de eșec pe care întregul sistem a fost proiectat să-l expună.
A afirma că „implementarea de referință scapă lucruri” doar pe baza inspecției surselor este exact genul de afirmație nedemonstrată pe care acest proiect există ca s-o elimine. Așa că am descărcat Java OSCAL CLI 3.2.0 din Maven Central, i-am verificat hash-ul și am rulat ambele validatoare pe o familie de fixture-uri ale căror documente de bază trec curat de ambele părți, astfel încât fiecare dezacord să poată fi atribuit unei singure modificări injectate.
OSCAL 1.2.2 a modificat exact două constrângeri dincolo de incrementările de versiune (ambele în modelul SSP). Java CLI, care încorporează legături 1.2.1, este de partea greșită la fiecare consecință observabilă:
rel="validation" care nu duce nicăieri încalcă 1.2.2. Java: valid, exit 0.rel="validated-by" care nu duce nicăieri este în regulă conform 1.2.2 (constrângerea a fost ștearsă). Java: invalid.responsible-role fără party-uuid, care este opțional, este în regulă conform 1.2.2; NIST a adăugat predicatul tocmai pentru a corecta acest lucru. Java dă eroare cu, textual, Key reference [null] not found.Apoi unul care nu are deloc legătură cu decalajul de versiune: un catalog al cărui action din metadate declară "type": "invented-type", interzis la linia 877 din Metaschema pentru metadate atât în 1.2.2, cât și în sursele 1.2.1 pe care le încorporează Java. Java îl declară valid, în JSON și în XML. Constrângerea vizează un flag; constrângerile care vizează flag-uri și nu se declanșează, fără niciun semnal, sunt exact clasa de bug-uri pe care poarta noastră de completitudine a prins-o în propriul nostru evaluator. Implementarea de referință are aceeași clasă de bug-uri și nicio poartă de închidere care să o prindă. Am raportat-o în amonte ca metaschema-framework/oscal-cli#279. (Am cartografiat clasa mecanic: exact 2 din 200 de apariții de allowed-values vizează flag-uri, iar singurul membru de nivel eroare este cel pe care l-am testat în mod real. Nu rămâne nimic netestat.)
Și momentul de onestitate, pentru că rularea a tăiat în ambele direcții: validatorul nostru a respins chiar catalogul SP 800-53 rev5 al NIST. Douăzeci de erori, toate provenind dintr-un singur defect: evaluatorul nostru de indexuri trata un câmp-cheie opțional lipsă ca pe o încălcare, în timp ce specificația Metaschema spune că este o cheie nulă. Java a acceptat fișierul; noi am greșit; am corectat în aceeași zi, iar 800-53 se validează curat. Ulterior, rularea a mai prins unul: continuam să impunem vocabularul OSCAL pentru tipurile de acțiuni chiar și atunci când action/@system declara un URI personalizat, adică exact segmentarea pe organizații pe care o cer chiar observațiile NIST. Corectat și acesta. Un harness diferențial care găsește doar bug-urile celeilalte părți nu este un harness, ci marketing.
(Tot la capitolul „ambele părți”: niciunul dintre rezolvatoare nu își valida semantic propriul rezultat. resolve-profile din Java va scrie liniștit un catalog pe care propriul validate al Java îl respinge apoi, iar al nostru făcea același lucru, cu o barieră la nivel de schemă, dar nu și la nivel semantic. Al nostru refuză acum să scrie rezolvări invalide semantic; cel din Java încă nu poate, pentru că închiderea acestei breșe necesită un strat semantic în care să aveți încredere.)
Al doilea lucru care lipsește din instrumentele existente: documentele OSCAL nu trăiesc izolat. Un rezultat al evaluării importă un plan de evaluare, care importă un SSP, care importă un profil, care se rezolvă în raport cu cataloage. Documentația modelului este plină de relații pe care niciun validator de fișiere individuale nu le poate verifica: „ținta acestei constatări trebuie să se rezolve la o declarație din nivelul de bază”, „acest POA&M trebuie să-și identifice sistemul”.
Așa că am construit o validare între documente care rezolvă efectiv aceste muchii. Cereți-i să verifice un SSP și va rezolva nivelul de bază importat prin întregul pipeline de rezolvare a profilului (conform specificației în stadiu de proiect) până la cataloage, va calcula setul de controale selectate și vă va spune:
{
"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'"
}
Cu o regulă de proiectare esențială: aceste verificări ale relațiilor sunt interpretări ale unui text documentat și le tratăm ca atare: dezactivate implicit, avertismente și nu erori, cu domeniul de rezolvare al fiecărui câmp fixat la linia sa din sursă, iar orice este doar deductibil este exclus nominal. Acolo unde standardul este evaziv, un validator nu ar trebui să blufeze. (Exemplul nostru preferat: specificația POA&M spune că este necesar un import de SSP sau un system-id: „Both may be present.” Așa că avertizăm doar când nu există niciunul. Fără un sau-exclusiv inventat.)
Pentru că documentele trăiesc în browsere, funcții serverless, CLI-uri și baze de date și am vrut un singur validator în toate acestea, nu un sidecar JVM lângă fiecare. Validatoarele sunt module Ajv standalone precompilate: JavaScript generat simplu, fără compilare de scheme la runtime, cu ieșire stabilă la nivel de octeți. Același cod care alimentează suprafețele noastre de validare rulează neschimbat în fila browserului dumneavoastră și în CLI-ul nostru. Iar determinismul nu este doar un avantaj operațional: el este cel care face posibile, în primul rând, dovezile fixate prin hash.
import { createDiagnosticsOscalProcessor } from "@secani/oscal/diagnostics";
const result = createDiagnosticsOscalProcessor({ maxIssues: 50 }).validate(doc);
validate --resolve-imports --interpretive all)Raportul tehnic complet, cu modelul de autoritate, închiderea completitudinii, tabelul cu dovezile diferențiale și ceea ce în mod deliberat nu afirmăm, se găsește în documentația validatorului. Validatorul se află într-un mediu de staging privat, înaintea unei lansări open-source; API-ul de validare găzduit expune astăzi nivelul de schemă, iar stratul semantic va urma.
Secani conectează domeniile de aplicare, dovezile, sarcinile și agenții IA într-un spațiu de lucru comun.
OLIR oferă conținutul corespondențelor și guvernanța acestora. OSCAL oferă structura lizibilă automat pentru utilizarea acestor corespondențe în analiza lacunelor, reutilizarea dovezilor și fluxurile de lucru privind impactul modificărilor.
OSCAL transformă documentele de conformitate în date structurate: opt modele de documente, trei formate și un ecosistem care devine standardul pentru reglementare.
Prin RFC-0024 și Consolidated Rules 2026, FedRAMP face obligatorii datele de autorizare structurate. Termenele sunt eșalonate, dar direcția este lipsită de echivoc.