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).

Edit JSON

Metadata

Tracking IDndaal-sa-2026-043
Categorycsaf_informational_advisory
CSAF Version2.1
Statusfinal
Version1.0.0
Publisherndaal Gesellschaft fuer Sicherheit in der Informationstechnik mbH & Co KG
TLPCLEAR
Initial Release2026-05-09T11:30:00.000Z
Current Release2026-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"
                }
              }
            ]
          }
        ]
      }
    ]
  }
}

Are you sure?