Cada validador de OSCAL afirma "validar OSCAL". Casi ninguno de ellos puede decirte qué significa esa frase.
Los modelos OSCAL del NIST están creados en Metaschema: ocho módulos raíz más importaciones compartidas, miles de líneas de XML que definen no solo la forma del documento sino también la semántica: vocabularios de valores permitidos, reglas de unicidad, índices de referencia cruzada, requisitos de cardinalidad. Un esquema JSON captura la forma. La semántica es donde los documentos de cumplimiento realmente fallan, y donde "validamos OSCAL" se convierte silenciosamente en "validamos parte de OSCAL, no estamos seguros de qué parte".
Queríamos estar seguros de qué parte. Así que construimos nuestro validador de la misma manera que auditarías un sistema: comenzamos desde las fuentes, enumeramos todo y contabilizamos cada elemento.
La primera decisión fue la importante: las fuentes del NIST son la autoridad, no la implementación de referencia. Fijamos usnistgov/OSCAL en el commit 21403b4a… (v1.2.2), almacenamos cada byte de Metaschema con hashes SHA-256 y tratamos la veterana CLI de Java OSCAL como lo que realmente es: un comparador al que someter a examen, no una verdad que copiar. Incorpora enlaces OSCAL 1.2.1: puede procesar documentos 1.2.2, pero no puede hablar 1.2.2.
Luego hicimos un inventario léxico de cada aparición de restricciones en las fuentes marcadas. No tipos de restricciones: ocurrencias, identificadas por archivo y línea, por lo que dos restricciones que comparten una ID nunca se fusionan en una. El recuento llegó a 348.
Para cada ocurrencia, nuestra compilación debe contener exactamente uno de tres veredictos, aplicados por un generador que vuelve a calcular el libro mayor en cada ejecución:
No contabilizado: cero.
En ese último segmento –las exclusiones– es donde se puso interesante.
Cuando verificas 348 restricciones una por una, encuentras cosas. Tres de nuestras exclusiones son defectos en las propias fuentes del NIST:
oscal_mapping-common_metaschema.xml La línea 657 restringe una bandera que se definió cinco líneas antes y luego nunca hace referencia a nada. Ningún documento que pueda existir lleva esta bandera. La restricción es insatisfactoria.inventory-item Los nombres de accesorios se aplican solo cuando @type es software, hardware, o service - pero inventory-item declara no type bandera en absoluto. (Su vecino, asset-id, está comentado en la fuente). El predicado nunca puede coincidir.with-child-controls. Un humano que lee la fuente los ve; el esquema compilado nunca los aplica. Los tres hallazgos se informan en sentido ascendente: usnistgov/OSCAL#2254, #2255, #2256.Y la auditoría fue en ambos sentidos: nuestro propio evaluador tenía un error en el que las restricciones dirigidas a banderas (como los valores permitidos de action/@type) compilado en el inventario pero silenciosamente nunca despedido. No lo encontramos por suerte: la puerta de cierre se negó a aceptar pruebas de esos dos sucesos, que es precisamente el modo de fallo que todo el sistema está diseñado para exponer.
Afirmar que "la implementación de referencia omite cosas" solo a partir de la inspección de la fuente es exactamente el tipo de afirmación no comprobada que este proyecto pretende eliminar. Así que descargamos Java OSCAL CLI 3.2.0 de Maven Central, lo verificamos mediante hash y ejecutamos ambos validadores sobre una familia de dispositivos cuyos documentos de referencia pasan limpiamente en ambos lados, por lo que cada desacuerdo es atribuible a un cambio inyectado.
OSCAL 1.2.2 cambió exactamente dos restricciones más allá de los cambios de versión (ambas en el modelo SSP). La CLI de Java, que incorpora enlaces 1.2.1, está en el lado equivocado de todas las consecuencias observables:
rel="validation" El enlace del componente viola 1.2.2. Java: válido, salida 0.rel="validated-by" el enlace está bien según 1.2.2 (la restricción fue eliminada). Java: no válido.responsible-role sin el opcional party-uuid está bien según 1.2.2: NIST agregó el predicado específicamente para solucionar este problema. Errores de Java con, literalmente, Key reference [null] not found.Luego, uno que no tiene nada que ver con la versión sesgada: un catálogo cuyos metadatos action declara "type": "invented-type" – prohibido en la línea 877 de los metadatos Metaschema tanto en las fuentes 1.2.2 como en las 1.2.1 que Java incorpora. Java lo declara válido, en JSON y XML. La restricción apunta a una bandera; Las restricciones dirigidas a banderas que no se activan silenciosamente es precisamente la clase de error que nuestra puerta de completitud detectó en nuestro propio evaluador. La implementación de referencia tiene la misma clase de error y no hay puerta de cierre para detectarlo. Lo reportamos aguas arriba como metaschema-framework/oscal-cli#279. (Asignamos la clase mecánicamente: exactamente 2 de 200 ocurrencias de valores permitidos apuntan a indicadores, y el único miembro con grado de error es el que probamos en vivo. No hay restos sin probar.)
Y la honestidad venció, porque la carrera fue en ambos sentidos: nuestro validador rechazó el catálogo SP 800-53 rev5 del NIST. Veinte errores, todos un defecto: nuestro evaluador de índices trató un campo de clave opcional faltante como una infracción donde la especificación de Metaschema dice que es una clave nula. Java aceptó el archivo; estábamos equivocados; Lo arreglamos el mismo día y 800-53 lo valida como limpio. Más tarde, la carrera encontró un segundo problema: seguimos aplicando el vocabulario de tipo de acción OSCAL incluso cuando action/@system declaró un URI personalizado: la segmentación exacta por organización que exigen los propios comentarios del NIST. También arreglado. Un arnés diferencial que sólo encuentra los errores del otro lado no es un arnés, es marketing.
(También archivado en "ambos lados": ninguno de los resolutores validó semánticamente su propia salida. Java resolve-profile Estaré encantado de escribir un catálogo propio de Java. validate luego rechaza, y el nuestro hizo lo mismo, controlado por esquemas pero no controlado por semántica. La nuestra se niega ahora a redactar resoluciones semánticamente inválidas; Java todavía no puede, porque cerrar ese agujero requiere una capa semántica en la que confíes).
La segunda cosa que falta en las herramientas existentes: los documentos OSCAL no viven solos. Un resultado de evaluación importa un plan de evaluación, que importa un SSP, que importa un perfil, que se resuelve en catálogos. La documentación del modelo está llena de relaciones que ningún validador de un solo archivo puede verificar: "el objetivo de este hallazgo debe resolverse en una declaración en la línea de base", "este POA&M debe identificar su sistema".
Por eso, creamos una validación entre documentos que resuelve esos bordes de verdad. Pídale que verifique un SSP y resolverá la línea base importada a través del canal de resolución de perfil completo (especificación de borrador) hasta los catálogos, calculará el conjunto de control seleccionado y le indicará:
{
"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'"
}
Con una regla de diseño crucial: estas comprobaciones de relaciones son interpretaciones de prosa documentada, y las tratamos de esa manera: desactivadas de forma predeterminada, advertencias, no errores, el alcance de resolución de cada campo fijado a su línea fuente y cualquier cosa meramente inferible excluida por nombre. Cuando el estándar protege, un validador no debería fanfarronear. (Nuestro ejemplo favorito: la especificación POA&M dice que se requiere una importación de SSP o una identificación del sistema: "Ambos pueden estar presentes". Por lo tanto, solo avisamos cuando ninguno existe. Ninguna exclusiva inventada.)
Porque los documentos residen en navegadores, funciones sin servidor, CLI y bases de datos, y queríamos un validador en todos ellos, no un sidecar JVM al lado de cada uno. Los validadores son módulos independientes de Ajv precompilados: JavaScript generado simple, compilación de esquema de tiempo de ejecución cero, salida estable en bytes. El mismo código que impulsa nuestras superficies de validación se ejecuta sin cambios en la pestaña de su navegador y en nuestra CLI. Y el determinismo no es sólo una sutileza de las operaciones: es lo que hace posible la evidencia codificada.
import { createDiagnosticsOscalProcessor } from "@secani/oscal/diagnostics";
const result = createDiagnosticsOscalProcessor({ maxIssues: 50 }).validate(doc);
validate --resolve-imports --interpretive all)El informe técnico completo (modelo de autoridad, cierre de integridad, tabla de ingresos diferenciales y lo que deliberadamente no reclamamos) vive en el documentación del validador. El validador se encuentra en fase privada antes de un lanzamiento de código abierto; el API de validación alojada expone el nivel del esquema actual, con la capa semántica a continuación.
Secani conecta Scopes, evidencias, tareas y agentes de IA en un espacio de trabajo compartido.
OLIR proporciona contenido cartográfico y gobernanza. OSCAL proporciona la estructura legible por máquina para utilizar esas asignaciones en el análisis de brechas, la reutilización de evidencia y los flujos de trabajo de impacto de cambios.
OSCAL convierte los documentos de cumplimiento en datos estructurados: ocho modelos de documentos, tres formatos y un ecosistema que se está convirtiendo en el estándar de regulación.
Con RFC-0024 y las Reglas Consolidadas 2026, FedRAMP hace que los datos de autorización estructurados sean obligatorios. Los plazos están escalonados y la dirección es inequívoca.