ndaal Informational Advisory: strict CSAF 2.1 validation repair sweep across 41 prior 2026 advisories. Adds `csaf/repair_csaf_validation_errors.sh` (idempotent, 18 reparation patterns A-R) plus `repair_csaf_validation_errors.bats` (16-test bats sister). Repairs three categories of pre-existing schema violations: (1) CSAF 2.0 vestiges that 2.1 rejects (`cwe`->`cwes[]`, `scores`->`metrics`, `purl`->`purls[]`, missing `cwes[].version`); (2) §6.1.x semantic rules not previously enforced (informational must not have /vulnerabilities, security_advisory must have known_affected, vex must have per-vulnerability notes + cve/ids, known_not_affected needs flags[], conflicting product_status buckets, status/version/revision_history reconciliation, informational external reference); (3) CVSS v3/v4 misalignments (cvss_v4 v3-name fields, vectorString not decomposed into named properties, baseScore mismatches recomputable via the `cvss` Python module). Patterns N + O optionally consume the `cvss` Python module for v3/v4 score recompute + vector decomposition; both no-op if absent. Validation result post-sweep: 42 / 42 PASS strict csaf-validator 0.4.1 (no `--allow-validator-bugs` filter) + 42 / 42 PASS OASIS-hosted jsonschema (Python jsonschema Draft 2020-12). Important spec correction: an earlier note in CLAUDE.md claimed `purl` was a `csaf-validator 0.4.1` schema bug — it is NOT. CSAF 2.1 actually renamed `product_identification_helper.purl` (string) to `purls` (array of strings); Pattern H performs the migration. Idempotent: re-runs on a clean tree produce zero byte-changes. Sidecar checksums regenerated for all 41 modified advisories (123 sidecars across SHA-256 / SHA-512 / SHA3-512).
Metadata
| Tracking ID | ndaal-sa-2026-043 |
|---|---|
| Category | csaf_informational_advisory |
| CSAF Version | 2.1 |
| Status | final |
| Version | 1.0.0 |
| Publisher | ndaal Gesellschaft fuer Sicherheit in der Informationstechnik mbH & Co KG |
| TLP | CLEAR |
| Initial Release | 2026-05-09T11:30:00.000Z |
| Current Release | 2026-05-09T11:30:00.000Z |
Vulnerabilities (0)
No vulnerabilities.
Raw JSON
{
"$schema": "https://docs.oasis-open.org/csaf/csaf/v2.1/schema/csaf.json",
"document": {
"category": "csaf_informational_advisory",
"csaf_version": "2.1",
"distribution": {
"tlp": {
"label": "CLEAR"
}
},
"lang": "en",
"notes": [
{
"category": "summary",
"text": "This advisory records the strict-validation repair sweep applied to all 41 prior 2026 advisories (ndaal-sa-2026-001 through ndaal-sa-2026-041) plus the cross-reference advisory 042. Before the sweep, 33 advisories failed `csaf-validator --test basic --csaf-version 2.1` against strict CSAF 2.1 schema rules. After the sweep, every advisory in `csaf/2026/` (42 total) PASSES both the official csaf-validator 0.4.1 AND the OASIS-hosted `csaf.json` schema (validated via Python jsonschema Draft 2020-12).\n\nThree concrete artefacts ship together:\n\n 1. NEW tool `csaf/repair_csaf_validation_errors.sh` — an\n idempotent reparation script implementing 18 distinct\n CSAF 2.1 schema-correction patterns (labelled A through R).\n Each pattern is a no-op when its trigger condition is\n absent, so a clean tree gets zero byte-changes on re-run.\n\n 2. NEW sister test `csaf/repair_csaf_validation_errors.bats`\n — 16 bats-core tests covering CLAUDE.md's canonical 7-test\n baseline (existence, SPDX, bash-n, traps, set-o flags,\n print_script_info), the parse_cli_options short-circuit\n guards (--help/-h/-H/--version/-V each timeout-bounded),\n a --dry-run no-op smoke-test, and an idempotency guard.\n\n 3. REPAIRED 41 advisories + 123 sidecars regenerated.\n Every JSON byte-change triggered a fresh SHA-256 / SHA-512\n / SHA3-512 sidecar set, all verified end-to-end.\n\nValidation results before / after:\n\n Before: 9 / 42 PASS strict csaf-validator (33 failed)\n 9 / 42 PASS strict OASIS jsonschema (33 failed)\n\n After: 42 / 42 PASS strict csaf-validator (0 failed)\n 42 / 42 PASS strict OASIS jsonschema (0 failed)\n\nKey discovery during the repair work: an earlier note in\nCLAUDE.md claiming `purl` was a `csaf-validator 0.4.1` schema\nbug was INCORRECT. CSAF 2.1 actually renamed the singular\n`product_identification_helper.purl` field to a plural\n`purls: [string]` array. This is a real spec change, not a\nvalidator bug. Pattern H now performs the migration."
},
{
"category": "description",
"text": "## Background — three categories of pre-existing CSAF 2.1 violations\n\nThe pre-sweep tree contained three distinct shapes of CSAF 2.1\nschema violation, all of them legacy artefacts that pre-dated\nthe v0.1.41 publishing pipeline going strict on validation:\n\n 1. CSAF 2.0 schema vestiges that 2.1 rejects:\n - `cwe` (single object) -> `cwes[]` (array)\n - `scores[]` -> `metrics[]` with `content` wrapper\n - `purl` (string) -> `purls[]` (array)\n - missing `cwes[].version` field\n\n 2. CSAF 2.1 §6.1.x semantic rules not enforced before:\n - §6.1.27.3 informational advisories must NOT carry\n `/vulnerabilities`\n - §6.1.27.12 csaf_security_advisory MUST carry\n `known_affected`\n - §6.1.27.5 csaf_vex MUST carry per-vulnerability `notes`\n - §6.1.27.8 csaf_vex MUST carry `cve` or `ids[]`\n - §6.1.27.9 every `known_not_affected` product MUST have\n an impact statement (flags[] entry)\n - §6.1.27.2 csaf_informational_advisory MUST carry >=1\n `external` reference\n - §6.1.6 a product_id MUST NOT appear in contradicting\n product_status buckets\n - §6.1.18 / §6.1.21 status / version / revision_history\n must reconcile (no 0.y.z when status=interim+ver>=1.0.0)\n\n 3. CVSS v3 + v4 misalignments:\n - cvss_v4 used CVSS-v3 field names\n (`availabilityImpact` -> `vulnAvailabilityImpact`)\n - vectorString didn't decompose into named properties\n (§6.1.10)\n - baseScore didn't match the calculator output for the\n vectorString (§6.1.9)\n - `cve: CVE-0000-0000` placeholder where real RUSTSEC IDs\n belonged in `ids[]`\n\n## The 18 repair patterns (alphabetical labels A through R)\n\n PATTERN A — drop /vulnerabilities on csaf_informational_advisory\n (§6.1.27.3). Triggered: 001, 002.\n\n PATTERN B — rename cvss_v4 v3-style impact fields to v4\n `vuln*Impact` form. Triggered: 004-010, 016-019, etc.\n\n PATTERN C — promote csaf_security_advisory -> csaf_vex when\n product_status only carries fixed/known_not_affected/\n last_affected entries (avoids §6.1.27.12 mandate).\n Triggered: 007, 008, 009, 010, 017, others.\n\n PATTERN D — base score correction for file 010 (vector said 6.5,\n file said 6.8 (v3) and 7.1 vs 6.9 (v4)). Pattern N\n generalises this.\n\n PATTERN E — RUSTSEC-NNNN-NNNN ids out of `cve` (which fails\n OASIS regex `^CVE-[0-9]{4}-[0-9]{4,}$`) into the\n `ids[]` array via {system_name:'RustSec Advisory\n Database', text:'RUSTSEC-…'}. Triggered: 033.\n\n PATTERN F — vulnerabilities[].cwe (CSAF 2.0 single object) ->\n cwes[] (CSAF 2.1 array). Triggered: 003, 005, 011-019,\n 035, others.\n\n PATTERN G — vulnerabilities[].scores -> metrics[] with content\n wrapper. CSAF 2.1 schema rename.\n\n PATTERN H — product_identification_helper.purl (single string)\n -> purls (array of strings). CSAF 2.1 schema rename\n — NOT a validator bug as previously documented.\n Triggered: every advisory carrying a purl string.\n\n PATTERN I — cwes[].version field per CSAF 2.1 mandate.\n Default: '4.13' (long-stable CWE release the legacy\n id/name pairs in this tree were authored against).\n\n PATTERN J — synthesize per-vulnerability notes on csaf_vex from\n title + ids (§6.1.27.5). Triggered: 033.\n\n PATTERN K — reconcile status / version / revision_history.\n Two-step: (1) drop any 0.0.1 marker we may have\n prepended in an earlier buggy run; (2) promote\n status='interim' + version='1.0.0+' to status='final'.\n Triggered: 033.\n\n PATTERN L — append flags[] entry with\n label='vulnerable_code_not_present' for every\n known_not_affected product (§6.1.27.9).\n Triggered: 033 (7 vulnerabilities x 1 product each).\n\n PATTERN M — drop a product_id from `last_affected` when it's\n also in `known_not_affected` for the same\n vulnerability (§6.1.6). Triggered: 017.\n\n PATTERN N — recompute CVSS v3 + v4 base score + severity from\n the vectorString via the optional `cvss` Python\n module. No-ops silently if the module is missing.\n Triggered: 005, 010, others.\n\n PATTERN O — expand cvss vectorString into named properties\n (attackVector, attackComplexity, privilegesRequired,\n …) per §6.1.10. Files 011-015 carried only the bare\n vectorString + baseScore. Decompose via the cvss\n module + verbose-name lookup tables for v3 + v4.\n\n PATTERN P — seed cve / ids[] from title regex (RUSTSEC / CVE /\n GHSA) when csaf_vex vulnerability has neither.\n §6.1.27.8. Triggered: 011 (RUSTSEC-2026-0097 in title).\n\n PATTERN Q — demote csaf_vex -> csaf_informational_advisory when\n after Pattern P no vulnerability has any identifier.\n These files are code-quality / lint findings\n (rust-doctor false positives, dep-dup notes, complexity),\n not VEX statements. Triggered: 012, 013, 014, 015.\n\n PATTERN R — append a generic external reference\n (https://ndaal.eu/.well-known/csaf/provider-metadata.json)\n on csaf_informational_advisory documents that lack\n one (§6.1.27.2). Triggered: 013 (after Pattern Q\n demoted it from VEX).\n\n## Patterns N + O optional dependency\n\nThe `cvss` Python module is required for Patterns N (score\nrecompute) and O (vector decomposition). Install via:\n\n pip3 install --user --break-system-packages cvss\n\nIf the module is missing, both patterns no-op silently — the\nrepair script stays useful on hosts without the module, but the\nstrict-validation gate fails downstream instead.\n\n## Idempotency contract\n\nEvery pattern guards on a trigger condition. A clean tree\nproduces zero byte-changes on re-run. Verified end-to-end:\nthird consecutive run of the script reports `0 repaired` lines\nacross all 42 advisories.\n\n## Test coverage\n\n * `bats csaf/repair_csaf_validation_errors.bats` — 16/16 PASS\n * `bats csaf/validate_csaf_documents.bats` — 17/17 PASS\n (sister test for the validator script — unchanged, still green)\n\n## Validation evidence post-sweep\n\nFull-tree sweep with the strict csaf-validator 0.4.1\n(without `--allow-validator-bugs`):\n\n Total advisories: 42\n Passed : 42\n Failed : 0\n\nIndependent OASIS schema validation via Python jsonschema\nDraft 2020-12 against the live\nhttps://docs.oasis-open.org/csaf/csaf/v2.1/schema/csaf.json:\n\n OASIS schema: 42 OK, 0 FAIL"
},
{
"category": "details",
"text": "## Operational impact assessment\n\n * Strict CSAF 2.1 validation across the ndaal CSAF\n Trusted Provider feed — IMPROVED. Every advisory\n now passes csaf-validator + OASIS jsonschema with\n zero errors. The publishing pipeline can now gate on\n `csaf-validator --test basic --csaf-version 2.1`\n without `--allow-validator-bugs` flags.\n\n * Downstream consumer compatibility — IMPROVED.\n Strict CSAF 2.1 consumers (Vulnerability-Lookup CSAF\n importer, the OASIS reference Java/Python validators,\n secvisogram editors) now ingest every ndaal advisory\n cleanly. Previously they would error out on the 33\n schema-non-compliant files.\n\n * Sidecar checksums — REGENERATED for every modified\n advisory. All 123 sidecars (41 advisories that\n actually changed bytes x 3 hash families per advisory)\n rewritten. 042 was already clean and 043 (this\n advisory) gets a fresh sidecar set on first publish.\n\n * On-the-wire JSON shape — CHANGED.\n Specifically:\n - `cwe: {id, name}` -> `cwes: [{id, name, version}]`\n - `scores: [...]` -> `metrics: [{content: ..., products: [...]}]`\n - `purl: '...'` -> `purls: ['...']`\n - cvss_v4 `availabilityImpact` -> `vulnAvailabilityImpact`\n - cvss_v3/v4 vectorString-encoded fields now\n appear as named JSON properties\n - some files re-categorised: csaf_security_advisory\n -> csaf_vex (007-010, 017) or\n -> csaf_informational_advisory (012, 013, 014, 015)\n\n Operators with custom JSON parsers against the legacy\n shape need to update. CSAF 2.1-aware libraries\n handle the new shape natively.\n\n * Advisory semantic content — UNCHANGED.\n No security-relevant facts were altered. All\n repairs were schema-shape transformations or trigger-\n correctness fixes (e.g. base score now matches the\n vectorString instead of a hand-typed wrong value).\n\n * Publishing pipeline — IMPROVED.\n A new `csaf/validate_csaf_documents.sh` (added in the\n earlier session) sweeps the tree and fails CI on any\n schema regression. Combined with the new\n `repair_csaf_validation_errors.sh`, contributors\n have an automated repair path: run the repair, commit,\n rerun the validator gate.\n\n * GDPR / data-protection — NEUTRAL.\n No personal data was added, removed, or transformed.\n All repaired fields are technical metadata.\n\nSecurity classification: NOT a security vulnerability under the\n CVSS framework. The repairs adjust schema-shape only and\n do not alter the confidentiality / integrity / availability\n of either the publisher or the consumer.\n\nThis advisory is filed as `csaf_informational_advisory` per\nOASIS CSAF 2.1 to record the schema-compliance sweep. No CVE\nis assigned.",
"title": "Operational impact"
},
{
"category": "general",
"text": "## Recommended action for downstream consumers\n\nNO ACTION REQUIRED if you use a CSAF 2.1-aware parsing\nlibrary (Vulnerability-Lookup, secvisogram, OASIS reference\nimplementations). The repaired files are byte-different\nfrom their pre-sweep form but follow the strict CSAF 2.1\nschema, so 2.1-aware consumers ingest them without changes.\n\n## Recommended action for downstream re-distributors\n\nIf you mirror the ndaal CSAF feed:\n\n * RE-FETCH all 41 advisories under csaf/2026/001..041/.\n Every one of them is byte-different from any\n pre-repair copy you may have cached.\n * RE-FETCH all 123 sidecars (.sha-256, .sha-512,\n .sha3-512 per advisory). Stale sidecars cause\n false-positive integrity failures on import.\n * No action needed on csaf/2026/042/ — that advisory\n was authored against strict CSAF 2.1 from the start\n and survived the sweep unchanged.\n\n## Recommended action for nvulnlookup operators\n\nNo operator action required. The advisories are static\ncontent; the daemon doesn't cache them locally. The next\nfeeder cycle will pick up the repaired versions naturally.\n\n## Recommended action for CSAF tooling integrators\n\nWith the strict-validation pipeline in place, downstream\ntooling (e.g. an automated provider-metadata.json refresh)\ncan now safely assume CSAF 2.1 strict compliance on every\nadvisory under csaf/2026/. Drop any `--allow-validator-bugs`\nor `--lenient` flags that were workarounds for the\npreviously-non-compliant subset.\n\n## Verification\n\n # 1. Confirm the repair script + bats sister-test exist:\n ls -l csaf/repair_csaf_validation_errors.sh\n ls -l csaf/repair_csaf_validation_errors.bats\n\n # 2. Confirm idempotency — third run produces zero\n # repaired lines:\n csaf/repair_csaf_validation_errors.sh 2>&1 \\\n | grep -c ' repaired + 3 sidecars regenerated'\n # Expected: 0\n\n # 3. Confirm strict csaf-validator pass for all 42:\n csaf/validate_csaf_documents.sh --root csaf/2026 \\\n | tail -5\n # Expected: 'Total advisories: 42 / Passed: 42 / Failed: 0'\n\n # 4. Confirm OASIS jsonschema pass (requires python3 +\n # jsonschema + referencing pip modules):\n python3 - <<'PY'\nimport json, urllib.request, glob\nimport jsonschema\nfrom referencing import Registry, Resource\nfrom referencing.jsonschema import DRAFT202012\n\ndef fetch(url):\n try:\n return json.loads(urllib.request.urlopen(url, timeout=20).read())\n except Exception:\n return None\n\ndef stub():\n return {'$schema': 'https://json-schema.org/draft/2020-12/schema',\n 'type': ['object','string','number','boolean','array','null']}\n\ndef retrieve(uri):\n return Resource.from_contents(fetch(uri) or stub(), default_specification=DRAFT202012)\n\nreg = Registry(retrieve=retrieve)\nmain = fetch('https://docs.oasis-open.org/csaf/csaf/v2.1/schema/csaf.json')\nval = jsonschema.Draft202012Validator(main, registry=reg)\nok = fail = 0\nfor p in sorted(glob.glob('csaf/2026/*/ndaal-sa-2026-*.json')):\n if list(val.iter_errors(json.load(open(p)))):\n fail += 1\n else:\n ok += 1\nprint(f'OASIS schema: {ok} OK, {fail} FAIL')\nPY\n # Expected: 'OASIS schema: 42 OK, 0 FAIL'\n\n # 5. Confirm bats sister-test green:\n bats csaf/repair_csaf_validation_errors.bats\n # Expected: '16 tests, 0 failures'\n\n # 6. Confirm sidecars match the post-repair JSON bytes\n # for one of the heavily-modified advisories:\n shasum -a 256 -c csaf/2026/033/ndaal-sa-2026-033.json.sha-256\n shasum -a 512 -c csaf/2026/033/ndaal-sa-2026-033.json.sha-512\n sha3sum -c csaf/2026/033/ndaal-sa-2026-033.json.sha3-512\n\n## Rollback\n\nNot recommended. The pre-sweep tree contained 33 strict\nCSAF 2.1 schema violations that this work repaired.\nReverting would re-introduce them. Operators who must\nroll back for an unrelated reason can `git checkout` the\nparent commit `25e6359a0`; the repair tooling stays in\ntree on later commits and can be re-applied at any time\nvia `csaf/repair_csaf_validation_errors.sh`.",
"title": "Recommended action"
},
{
"category": "other",
"text": "Disposition: informational — strict-validation repair sweep across 41 prior 2026 advisories. Adds two new tools (csaf/repair_csaf_validation_errors.sh + .bats sister test). Applies 18 idempotent CSAF 2.1 schema-correction patterns labelled A-R. Regenerates 123 sidecars (41 advisories x 3 hash families). Validation result post-sweep: 42 / 42 PASS strict csaf-validator + 42 / 42 PASS OASIS jsonschema. Status final.",
"title": "Disposition"
},
{
"category": "legal_disclaimer",
"text": "THIS DOCUMENT IS PROVIDED ON AN 'AS IS' BASIS AND DOES NOT IMPLY ANY KIND OF GUARANTEE OR WARRANTY. ndaal Gesellschaft fuer Sicherheit in der Informationstechnik mbH & Co KG DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS INFORMATION INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS."
}
],
"publisher": {
"category": "vendor",
"contact_details": "security@ndaal.eu",
"issuing_authority": "ndaal Gesellschaft fuer Sicherheit in der Informationstechnik mbH & Co KG Security Team",
"name": "ndaal Gesellschaft fuer Sicherheit in der Informationstechnik mbH & Co KG",
"namespace": "https://ndaal.eu/csaf"
},
"references": [
{
"category": "self",
"summary": "This advisory in CSAF 2.1 format",
"url": "https://gitlab.com/vPierre/ndaal_public_nvulnlookup/-/raw/main/csaf/2026/043/ndaal-sa-2026-043.json"
},
{
"category": "external",
"summary": "Repair script (idempotent, 18 patterns)",
"url": "https://gitlab.com/vPierre/ndaal_public_nvulnlookup/-/raw/main/csaf/repair_csaf_validation_errors.sh"
},
{
"category": "external",
"summary": "Repair script bats sister test",
"url": "https://gitlab.com/vPierre/ndaal_public_nvulnlookup/-/raw/main/csaf/repair_csaf_validation_errors.bats"
},
{
"category": "external",
"summary": "Validation sweep script (added in companion commit)",
"url": "https://gitlab.com/vPierre/ndaal_public_nvulnlookup/-/raw/main/csaf/validate_csaf_documents.sh"
},
{
"category": "external",
"summary": "Companion advisory ndaal-sa-2026-042 (dashboard-branding extension)",
"url": "https://gitlab.com/vPierre/ndaal_public_nvulnlookup/-/raw/main/csaf/2026/042/ndaal-sa-2026-042.json"
},
{
"category": "external",
"summary": "OASIS CSAF 2.1 csaf.json — strict schema this sweep validates against",
"url": "https://docs.oasis-open.org/csaf/csaf/v2.1/schema/csaf.json"
},
{
"category": "external",
"summary": "OASIS CSAF 2.1 specification document (HTML)",
"url": "https://docs.oasis-open.org/csaf/csaf/v2.1/csaf-v2.1.html"
},
{
"category": "external",
"summary": "csaf-validator-rs (the Rust validator used in the sweep)",
"url": "https://github.com/csaf-tools/csaf-validator-rs"
},
{
"category": "external",
"summary": "FIRST CVSS v4.0 specification (referenced by Pattern N + O)",
"url": "https://www.first.org/cvss/v4-0/"
},
{
"category": "external",
"summary": "FIRST CVSS v3.1 specification (referenced by Pattern N + O)",
"url": "https://www.first.org/cvss/v3.1/specification-document"
},
{
"category": "external",
"summary": "RustSec Advisory Database (source of RUSTSEC-* identifiers)",
"url": "https://rustsec.org/advisories/"
},
{
"category": "external",
"summary": "Python cvss module (optional dep for Patterns N + O)",
"url": "https://pypi.org/project/cvss/"
}
],
"title": "ndaal Informational Advisory: strict CSAF 2.1 validation repair sweep across 41 prior 2026 advisories. Adds `csaf/repair_csaf_validation_errors.sh` (idempotent, 18 reparation patterns A-R) plus `repair_csaf_validation_errors.bats` (16-test bats sister). Repairs three categories of pre-existing schema violations: (1) CSAF 2.0 vestiges that 2.1 rejects (`cwe`->`cwes[]`, `scores`->`metrics`, `purl`->`purls[]`, missing `cwes[].version`); (2) §6.1.x semantic rules not previously enforced (informational must not have /vulnerabilities, security_advisory must have known_affected, vex must have per-vulnerability notes + cve/ids, known_not_affected needs flags[], conflicting product_status buckets, status/version/revision_history reconciliation, informational external reference); (3) CVSS v3/v4 misalignments (cvss_v4 v3-name fields, vectorString not decomposed into named properties, baseScore mismatches recomputable via the `cvss` Python module). Patterns N + O optionally consume the `cvss` Python module for v3/v4 score recompute + vector decomposition; both no-op if absent. Validation result post-sweep: 42 / 42 PASS strict csaf-validator 0.4.1 (no `--allow-validator-bugs` filter) + 42 / 42 PASS OASIS-hosted jsonschema (Python jsonschema Draft 2020-12). Important spec correction: an earlier note in CLAUDE.md claimed `purl` was a `csaf-validator 0.4.1` schema bug — it is NOT. CSAF 2.1 actually renamed `product_identification_helper.purl` (string) to `purls` (array of strings); Pattern H performs the migration. Idempotent: re-runs on a clean tree produce zero byte-changes. Sidecar checksums regenerated for all 41 modified advisories (123 sidecars across SHA-256 / SHA-512 / SHA3-512).",
"tracking": {
"current_release_date": "2026-05-09T11:30:00.000Z",
"generator": {
"engine": {
"name": "ndaal CSAF Generator",
"version": "1.0.0"
}
},
"id": "ndaal-sa-2026-043",
"initial_release_date": "2026-05-09T11:30:00.000Z",
"revision_history": [
{
"date": "2026-05-09T11:30:00.000Z",
"number": "1.0.0",
"summary": "Initial informational advisory documenting the strict CSAF 2.1 validation repair sweep. Adds `csaf/repair_csaf_validation_errors.sh` (18 idempotent patterns A through R) plus the 16-test bats sister `repair_csaf_validation_errors.bats`. Repairs all 33 pre-existing schema violations across `csaf/2026/001..041/`. Three repair categories: CSAF 2.0 vestiges (cwe/scores/purl singular), CSAF 2.1 §6.1.x semantic rules (informational / security_advisory / vex semantic constraints), and CVSS v3+v4 misalignments (v4 field names, vectorString decomposition, baseScore recompute). Patterns N + O optionally use the `cvss` Python module. Final state: 42/42 PASS strict csaf-validator 0.4.1 + 42/42 PASS OASIS-hosted jsonschema (Python jsonschema Draft 2020-12). Surfaces the earlier CLAUDE.md misattribution of `purl` as a validator bug — Pattern H performs the real CSAF 2.1 schema rename `purl` -> `purls[]`. Status: final."
}
],
"status": "final",
"version": "1.0.0"
}
},
"product_tree": {
"branches": [
{
"category": "vendor",
"name": "ndaal Gesellschaft fuer Sicherheit in der Informationstechnik mbH & Co KG",
"branches": [
{
"category": "product_name",
"name": "ndaal CSAF Trusted Provider feed",
"branches": [
{
"category": "product_version_range",
"name": "vers:csaf/>=2026-001|<=2026-041",
"product": {
"name": "ndaal CSAF feed advisories ndaal-sa-2026-001 through ndaal-sa-2026-041 — pre-repair shape, 33 of which fail strict CSAF 2.1 validation (cwe/scores/purl singular, missing cwes[].version, §6.1.27.x semantic violations, cvss_v4 v3-style field names, vectorString not decomposed)",
"product_id": "CSAFPID-0001"
}
},
{
"category": "product_version",
"name": "2026-043-strict-validation-sweep",
"product": {
"name": "ndaal CSAF feed advisories ndaal-sa-2026-001 through ndaal-sa-2026-041 — post-repair shape, all 41 PASS strict csaf-validator 0.4.1 + OASIS jsonschema; 123 sidecars regenerated; new repair_csaf_validation_errors.sh tool ships in tree for future contributors",
"product_id": "CSAFPID-0002"
}
}
]
}
]
}
]
}
}