
Всеки валидатор за OSCAL твърди, че „валидира OSCAL“. Почти никой от тях не може да ви каже какво означава това изречение.
Моделите на OSCAL на NIST са написани в Metaschema – осем коренни модула плюс споделени импорти, хиляди редове XML, които определят не само формата на документите, но и семантиката: речници с разрешени стойности, правила за уникалност, индекси за кръстосани препратки, изисквания за кардиналност. JSON Schema улавя формата. Семантиката е мястото, където документите за съответствие действително се объркват – и където „валидираме OSCAL“ тихомълком се превръща във „валидираме част от OSCAL, не сме сигурни коя“.
Искахме да сме сигурни коя част. Затова изградихме нашия валидатор така, както бихте одитирали система: започнете от изходните файлове, изброете всичко и отчетете всеки отделен елемент.
Първото решение беше най-важното: авторитетът са изходните файлове на NIST, а не референтната реализация. Закрепихме usnistgov/OSCAL на commit 21403b4a… (v1.2.2), кеширахме всеки байт на Metaschema точно, със SHA-256 хешове, и третирахме почтения Java OSCAL CLI като това, което той всъщност е: обект за сравнение, който да подложим на кръстосан разпит, а не истина, която да копираме. (Между другото, той вгражда bindings за OSCAL 1.2.1 – може да обработва документи 1.2.2, но не може да говори 1.2.2.)
След това направихме лексикален опис на всяко срещане на ограничение в закрепените изходни файлове. Не на типовете ограничения, а на срещанията, идентифицирани по файл и ред, така че две ограничения, които случайно имат еднакъв ID, никога да не се слеят в едно. Броят се оказа 348.
За всяко срещане нашата компилация трябва да съдържа точно една от три присъди, наложени от генератор, който преизчислява регистъра при всяко изпълнение:
Неотчетени: нула.
Тази последна група – изключенията – се оказа най-интересната.
Когато проверявате 348 ограничения едно по едно, откривате разни неща. Три от нашите изключения са дефекти в самите изходни файлове на NIST:
oscal_mapping-common_metaschema.xml, ред 657, ограничава флаг, който е дефиниран пет реда по-рано – и след това никога не се препраща от нищо. Нито един документ, който може да съществува, не съдържа този флаг. Ограничението е неизпълнимо.inventory-item се прилага само когато @type е software, hardware или service – но inventory-item изобщо не декларира флаг type. (Неговият съсед, asset-id, сам е закоментиран в изходния файл.) Предикатът никога не може да съвпадне.with-child-controls в профила. Човек, който чете изходния файл, ги вижда; компилираната схема никога не ги прилага. И трите констатации са докладвани нагоре по веригата: usnistgov/OSCAL#2254, #2255, #2256.А одитът проряза и в двете посоки: нашият собствен оценител имаше грешка, при която ограниченията, насочени към флагове (като разрешените стойности на action/@type), се компилираха в описа, но тихомълком никога не се задействаха. Не я открихме случайно – проверката за пълнота отказа да приеме доказателства за тези две срещания, което е точно видът отказ, който цялата система е проектирана да разкрива.
Да твърдиш, че „референтната реализация пропуска неща“, само въз основа на преглед на изходния код, е точно онзи вид недоказано твърдение, което този проект съществува, за да елиминира. Затова изтеглихме Java OSCAL CLI 3.2.0 от Maven Central, проверихме хеша му и пуснахме двата валидатора върху семейство от тестови документи, чиито базови документи преминават чисто и от двете страни – така всяко разминаване може да се припише на една въведена промяна.
OSCAL 1.2.2 промени точно две ограничения освен повишаването на версиите (и двете в модела SSP). Java CLI – който вгражда bindings за 1.2.1 – е на грешната страна при всяка наблюдаема последица:
rel="validation" нарушава 1.2.2. Java: валиден, код за изход 0.rel="validated-by" е наред според 1.2.2 (ограничението е изтрито). Java: невалиден.responsible-role без незадължителния party-uuid е наред според 1.2.2 – NIST добави предиката специално, за да поправи това. Java връща грешка с буквално Key reference [null] not found.После един случай, който изобщо не е свързан с разминаването във версиите: каталог, чийто action в метаданните декларира "type": "invented-type" – забранено на ред 877 от Metaschema за метаданните както в 1.2.2, така и в изходните файлове на 1.2.1, които Java вгражда. Java го обявява за валиден, в JSON и в XML. Ограничението е насочено към флаг; ограничения, насочени към флагове, които тихомълком не се задействат, са точно класът грешки, който нашата проверка за пълнота улови в собствения ни оценител. Референтната реализация има същия клас грешки – и няма проверка за пълнота, която да ги улови. Докладвахме го нагоре по веригата като metaschema-framework/oscal-cli#279. (Картографирахме класа механично: точно 2 от 200 срещания на allowed-values са насочени към флагове, а единственият член с ниво на грешка е този, който тествахме на живо. Не остава нищо нетествано.)
И моментът на честност, защото изпълнението проряза и в двете посоки: нашият валидатор отхвърли собствения каталог на NIST SP 800-53 rev5. Двадесет грешки, всичките от един дефект – нашият оценител на индекси третираше липсващо незадължително ключово поле като нарушение, докато спецификацията на Metaschema казва, че това е нулев ключ. Java прие файла; ние грешахме; поправихме го същия ден и 800-53 се валидира чисто. По-късно изпълнението улови и втори случай: продължавахме да прилагаме речника на OSCAL за типовете action дори когато action/@system декларираше собствен URI – точно сегментирането по организации, което призовават собствените забележки на NIST. И това е поправено. Диференциална тестова рамка, която винаги открива само грешките на другата страна, не е тестова рамка, а маркетинг.
(Също в категорията „и от двете страни“: нито един от двата резолвера не валидираше семантично собствения си резултат. resolve-profile на Java спокойно ще запише каталог, който собственият validate на Java след това отхвърля – а нашият правеше същото, с проверка на схемата, но без проверка на семантиката. Нашият вече отказва да записва семантично невалидни резолюции; Java все още не може, защото затварянето на тази дупка изисква семантичен слой, на който имате доверие.)
Второто нещо, което липсва в съществуващите инструменти: документите на OSCAL не живеят сами. Резултатът от оценка импортира план за оценка, който импортира SSP, който импортира профил, който се разрешава спрямо каталози. Документацията на модела е пълна с връзки, които никой валидатор на единичен файл не може да провери – „целта на тази констатация трябва да се разреши до твърдение в базовата линия“, „този POA&M трябва да идентифицира своята система“.
Затова изградихме валидиране между документи, което наистина разрешава тези връзки. Поискайте от него да провери SSP и той разрешава импортираната базова линия през пълния конвейер за разрешаване на профили (по проекта на спецификацията) до каталозите, изчислява избрания набор от контроли и ви казва:
{
"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'"
}
С едно решаващо правило в дизайна: тези проверки на връзките са тълкувания на документиран текст и ние ги третираме като такива – изключени по подразбиране, предупреждения, а не грешки, обхватът на разрешаване на всяко поле е закрепен към изходния ред, а всичко, което само може да се изведе, е изключено поименно. Там, където стандартът се изразява уклончиво, валидаторът не бива да блъфира. (Нашият любим пример: спецификацията на POA&M казва, че се изисква импорт на SSP или system-id – „Both may be present.“ Затова предупреждаваме само когато нито едно от двете не съществува. Никакво измислено изключващо „или“.)
Защото документите живеят в браузъри, serverless функции, CLI инструменти и бази данни – и искахме един валидатор във всички тях, а не JVM sidecar до всеки от тях. Валидаторите са предварително компилирани самостоятелни модули на Ajv: обикновен генериран JavaScript, нулева компилация на схеми по време на изпълнение, побайтово стабилен резултат. Същият код, който захранва нашите повърхности за валидиране, работи непроменен във вашия раздел на браузъра и в нашия CLI. А детерминизмът не е просто оперативно удобство – той е това, което изобщо прави възможни доказателствата, закрепени с хеш.
import { createDiagnosticsOscalProcessor } from "@secani/oscal/diagnostics";
const result = createDiagnosticsOscalProcessor({ maxIssues: 50 }).validate(doc);
validate --resolve-imports --interpretive all)Пълният технически доклад – модел на авторитета, проверка за пълнота, таблица с разписките от диференциалното сравнение и какво съзнателно не твърдим – се намира в документацията на валидатора. Валидаторът е в частна предварителна среда преди пускането му с отворен код; хостваният API за валидиране предоставя днес нивото на схемата, а семантичният слой ще последва.
Secani свързва обхвати, доказателства, задачи и агенти с ИИ в едно споделено работно пространство.
OLIR предоставя съдържание за съпоставките и управление. OSCAL предоставя машинночетимата структура за използването на тези съпоставки при анализ на пропуските, повторна употреба на доказателства и оценка на въздействието от промени.
OSCAL превръща документите за съответствие в структурирани данни: осем модела на документи, три формата и екосистема, която се превръща в стандарт за регулирането.
С RFC-0024 и Consolidated Rules 2026 FedRAMP прави структурираните данни за оторизация задължителни. Сроковете са поетапни – посоката е недвусмислена.