Chaque validateur OSCAL prétend « valider OSCAL ». Presque aucun d’entre eux ne peut vous dire ce que signifie cette phrase.
Les modèles OSCAL du NIST sont créés dans Metaschema : huit modules racine plus des importations partagées, des milliers de lignes XML qui définissent non seulement la forme du document mais aussi la sémantique : vocabulaires de valeurs autorisés, règles d'unicité, index de références croisées, exigences de cardinalité. Un schéma JSON capture la forme. C'est dans la sémantique que les documents de conformité tournent mal – et là où « nous validons OSCAL » devient discrètement « nous validons une partie d'OSCAL, nous ne savons pas quelle partie ».
Nous voulions être sûrs de quelle partie. Nous avons donc construit notre validateur de la même manière que vous auditiez un système : commencez par les sources, énumérez tout et prenez en compte chaque élément.
La première décision était la plus importante : les sources du NIST font autorité – pas l'implémentation de référence. Nous avons épinglé usnistgov/OSCAL à la validation 21403b4a… (v1.2.2), a mis en cache chaque octet de Metaschema avec les hachages SHA-256 et a traité la vénérable Java OSCAL CLI comme ce qu'elle est réellement : un comparateur à contre-interroger, pas une vérité à copier. (Il intègre les liaisons OSCAL 1.2.1, d'une part – il peut traiter les documents 1.2.2, mais il ne peut pas parler 1.2.2.)
Ensuite, nous avons inventorié lexicalement chaque occurrence de contrainte dans les sources épinglées. Pas de types de contraintes – occurrences, identifiées par fichier et ligne, de sorte que deux contraintes qui partagent un ID ne se réduisent jamais en une seule. Le décompte s'élève à 348.
Pour chaque occurrence, notre build doit contenir exactement l'un des trois verdicts, appliqués par un générateur qui recalcule le grand livre à chaque exécution :
Personne disparue : zéro.
C’est dans cette dernière catégorie – les exclusions – que les choses sont devenues intéressantes.
Lorsque vous vérifiez 348 contraintes une par une, vous trouvez des choses. Trois de nos exclusions sont des défauts dans les sources NIST elles-mêmes :
oscal_mapping-common_metaschema.xml la ligne 657 contraint un indicateur défini cinq lignes plus tôt – et qui n'a jamais été référencé par quoi que ce soit. Aucun document qui puisse exister ne porte ce drapeau. La contrainte est insatisfaisante.inventory-item les noms d'accessoires ne s'appliquent que lorsque @type est software, hardware, ou service - mais inventory-item déclare non type drapeau du tout. (Son voisin, asset-id, est lui-même commenté dans la source.) Le prédicat ne peut jamais correspondre.with-child-controls. Un humain lisant la source les voit ; le schéma compilé ne les applique jamais. Les trois résultats sont rapportés en amont : usnistgov/OSCAL#2254, #2255, #2256.Et l'audit a été à double tranchant : notre propre évaluateur avait un bug où les contraintes ciblant les flags (comme les valeurs autorisées de action/@type) compilé dans l'inventaire mais n'a jamais tiré en silence. Nous ne l'avons pas trouvé par hasard – la porte de fermeture a refusé d'accepter les preuves de ces deux événements, ce qui est précisément le mode de défaillance que l'ensemble du système est conçu pour exposer.
Affirmer que "l'implémentation de référence manque des choses" à partir de la seule inspection à la source est exactement le genre d'affirmation non prouvée que ce projet existe pour tuer. Nous avons donc téléchargé Java OSCAL CLI 3.2.0 depuis Maven Central, l'avons vérifié par hachage et exécuté les deux validateurs sur une famille de luminaires dont les documents de base passent proprement des deux côtés - de sorte que chaque désaccord est attribuable à un changement injecté.
OSCAL 1.2.2 a modifié exactement deux contraintes au-delà des modifications de version (toutes deux dans le modèle SSP). La CLI Java – qui intègre les liaisons 1.2.1 – se trouve du mauvais côté de toutes les conséquences observables :
rel="validation" le lien de composant viole 1.2.2. Java : valide, quittez 0.rel="validated-by" le lien est bien selon 1.2.2 (la contrainte a été supprimée). Java : invalide.responsible-role sans le facultatif party-uuid c'est bien selon 1.2.2 - Le NIST a ajouté le prédicat spécifiquement pour résoudre ce problème. Erreurs Java avec, littéralement, Key reference [null] not found.Ensuite, un qui ne concerne pas du tout le biais de version : un catalogue dont les métadonnées action déclare "type": "invented-type" – interdit à la ligne 877 du métaschéma de métadonnées dans les sources 1.2.2 et 1.2.1 intégrées par Java. Java le déclare valide, en JSON et XML. La contrainte cible un drapeau ; les contraintes ciblées sur les indicateurs qui ne se déclenchent pas silencieusement sont précisément la classe de bogues que notre porte d'exhaustivité a détectée dans notre propre évaluateur. L'implémentation de référence a la même classe de bugs – et aucune porte de fermeture pour l'attraper. Nous l'avons signalé en amont comme métaschéma-framework/oscal-cli#279. (Nous avons cartographié la classe mécaniquement : exactement 2 des 200 occurrences de valeurs autorisées ciblent les indicateurs, et le seul membre de niveau d'erreur est celui que nous avons testé en direct. Pas de reste non testé.)
Et l'honnêteté a battu, parce que la course a été dans les deux sens : notre validateur a rejeté le propre catalogue SP 800-53 rev5 du NIST. Vingt erreurs, un seul défaut : notre évaluateur d'index a traité un champ de clé facultatif manquant comme une violation alors que la spécification Metaschema indique qu'il s'agit d'une clé nulle. Java a accepté le fichier ; nous avions tort ; nous l'avons réparé le même jour, et 800-53 valide le nettoyage. La course en a ensuite attrapé une deuxième : nous avons continué à appliquer le vocabulaire de type action OSCAL même lorsque action/@system a déclaré un URI personnalisé – la segmentation exacte par organisation réclamée par les propres remarques du NIST. Également corrigé. Un harnais différentiel qui ne trouve que les bugs de l'autre côté n'est pas un harnais, c'est du marketing.
(Également classé sous « les deux côtés » : aucun des deux résolveurs n'a validé sémantiquement sa propre sortie. Java resolve-profile j'écrirai avec plaisir un catalogue propre à Java validate puis rejette – et le nôtre a fait de même, en fonction du schéma mais pas de la sémantique. La nôtre refuse désormais d’écrire des résolutions sémantiquement invalides ; Java ne le peut toujours pas, car combler ce trou nécessite une couche sémantique de confiance.)
Deuxième chose qui manque aux outils existants : les documents OSCAL ne vivent pas seuls. Un résultat d'évaluation importe un plan d'évaluation, qui importe un SSP, qui importe un profil, qui se résout par rapport aux catalogues. La documentation du modèle regorge de relations qu'aucun validateur mono-fichier ne peut vérifier : « la cible de ce résultat doit se résoudre en une déclaration dans la ligne de base », « ce POA&M doit identifier son système ».
Nous avons donc créé une validation inter-documents qui résout réellement ces limites. Demandez-lui de vérifier un SSP, et il résout la ligne de base importée via le pipeline de résolution de profil complet (spécification préliminaire) jusqu'aux catalogues, calcule l'ensemble de contrôles sélectionné et vous indique :
{
"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'"
}
Avec une règle de conception cruciale : ces vérifications de relations sont des interprétations de prose documentée, et nous les traitons de cette façon : désactivées par défaut, avertissements et non erreurs, la portée de résolution de chaque champ épinglée sur sa ligne source et tout ce qui est simplement inférable exclu par son nom. Là où les couvertures sont standards, un validateur ne devrait pas bluffer. (Notre exemple préféré : la spécification POA&M indique qu'une importation SSP ou un identifiant système est requis – "Les deux peuvent être présents." Nous avertissons donc uniquement lorsque ni n'existe. Pas de ou exclusif inventé.)
Parce que les documents se trouvent dans des navigateurs, des fonctions sans serveur, des CLI et des bases de données – et nous voulions un validateur dans chacun d'eux, pas un side-car JVM à côté de chacun. Les validateurs sont des modules autonomes Ajv précompilés : JavaScript généré en clair, compilation de schéma sans exécution, sortie stable en octets. Le même code qui alimente nos surfaces de validation s'exécute inchangé dans l'onglet de votre navigateur et dans notre CLI. Et le déterminisme n’est pas seulement une subtilité opérationnelle – c’est ce qui rend possible la preuve hachée.
import { createDiagnosticsOscalProcessor } from "@secani/oscal/diagnostics";
const result = createDiagnosticsOscalProcessor({ maxIssues: 50 }).validate(doc);
validate --resolve-imports --interpretive all)Le rapport technique complet – modèle d’autorité, clôture d’exhaustivité, tableau des recettes différentielles et ce que nous ne prétendons délibérément pas – vit dans le documentation du validateur. Le validateur est en phase privée avant une version open source ; le API de validation hébergée expose le niveau du schéma aujourd'hui, avec la couche sémantique à suivre.
Secani réunit Scopes, preuves, tâches et agents d’IA dans un espace de travail partagé.
OLIR fournit du contenu cartographique et de la gouvernance. OSCAL fournit la structure lisible par machine pour utiliser ces mappages dans l'analyse des lacunes, la réutilisation des preuves et les flux de travail sur l'impact des changements.
OSCAL transforme les documents de conformité en données structurées : huit modèles de documents, trois formats et un écosystème qui devient la norme en matière de réglementation.
Avec la RFC-0024 et les règles consolidées 2026, FedRAMP rend obligatoires les données d'autorisation structurées. Les délais sont échelonnés – la direction est sans ambiguïté.