Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions .github/workflows/reference-data-refresh.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ jobs:
- name: Install requirements
run: python -m pip install --quiet -r data_validation/requirements.txt

- name: Regenerate ATLAS and CWE tables
- name: Regenerate the reference tables
env:
GITHUB_TOKEN: ${{ github.token }}
run: python data_validation/reference_data/update_reference_data.py

- name: Run the checks against the new tables
Expand All @@ -50,11 +52,11 @@ jobs:
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git switch -c "$branch"
git add data_validation/reference_data
git commit -m "reference_data: refresh MITRE ATLAS and CWE tables"
git commit -m "reference_data: refresh the externally maintained tables"
git push --force origin "$branch"
versions=$(grep -E '^\| (MITRE ATLAS|CWE) ' data_validation/reference_data/SOURCES.md | cut -d'|' -f2,4)
versions=$(grep -E '^\| (MITRE ATLAS|CWE|AIBOM Generator fields) ' data_validation/reference_data/SOURCES.md | cut -d'|' -f2,4)
{
echo "The monthly refresh regenerated the MITRE ATLAS and CWE tables."
echo "The monthly refresh regenerated the MITRE ATLAS, CWE and AIBOM Generator field tables."
echo
echo "Versions: $versions"
echo
Expand Down
3 changes: 2 additions & 1 deletion data_validation/reference_data/SOURCES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ Lookup tables the validators check identifiers against.
| Language subtags | `language_subtags.csv` | 2026-09-17 | IANA Language Subtag Registry, `https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry` (languages, scripts, regions) |
| NIST AI 100-2 | `nist_ai_100_2.csv` | E2025 | NIST AI 100-2e2025, *Adversarial Machine Learning: A Taxonomy and Terminology of Attacks and Mitigations*, Taxonomy Index, https://doi.org/10.6028/NIST.AI.100-2e2025. The 30 `NISTAML.*` identifiers from its Taxonomy Index and section text (NISTAML.016 and .017 appear only in §3.4.1) |
| CWE | `cwe_ids.csv` | 4.20 | `https://cwe.mitre.org/data/xml/cwec_latest.xml.zip` (weaknesses, categories and views) |
| AIBOM Generator fields | `aibom_fields.csv` | aibom-generator@45851cb96dbe | OWASP AIBOM Generator `src/models/field_registry.json`, `https://github.com/GenAI-Security-Project/aibom-generator`: one row per field id, with the CycloneDX path the generator writes it to (`none` when it never writes the field to the BOM), its inclusion rule and its tier |

`owasp_top10.csv` is maintained by hand. The 2026 LLM list renumbered the entries (LLM03 was Supply Chain in 2025 and is Excessive Agency in 2026), so IDs always carry the edition year; `equivalent_2025` gives the 2025 ID each 2026 entry continues (LLM08:2026 Hidden Context Exposure broadens LLM07:2025 System Prompt Leakage). OWASP writes the Agentic IDs without a year (ASI01); this repository adds `:2026` so a future edition cannot collide.

Regenerate the ATLAS, CWE, SPDX and language tables with `python update_reference_data.py`; it fills in the Version column. Do not edit those CSVs by hand. `nist_ai_100_2.csv` and `owasp_top10.csv` are maintained by hand when a new edition is published.
Regenerate the ATLAS, CWE, SPDX, language and AIBOM field tables with `python update_reference_data.py`; it fills in the Version column. Do not edit those CSVs by hand. `nist_ai_100_2.csv` and `owasp_top10.csv` are maintained by hand when a new edition is published.

`datasets/_shared/dsgai_taxonomy.json` is the DSGAI list the validators use. `dsgai_entries.json` is kept because the Turkish prompt-injection validator reads it.

Expand Down
51 changes: 51 additions & 0 deletions data_validation/reference_data/aibom_fields.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
field_id,cyclonedx_path,rule,tier,description
attention_head_count,$.components[0].properties,include_if_available,supplementary,Number of attention heads in the model
attention_head_count_kv,$.components[0].properties,include_if_available,supplementary,Number of Key-Value attention heads
autonomyType,$.metadata.properties,include_if_available,supplementary,Level of autonomy or human involvement required
block_count,$.components[0].properties,include_if_available,supplementary,Number of transformer blocks or layers
bomFormat,$.bomFormat,always_include,critical,Format identifier for the SBOM
component_version,$.components[0].version,always_include,critical,Version of the component
context_length,$.components[0].properties,include_if_available,supplementary,Maximum context length or sequence length supported
datasets,$.component.modelCard.modelParameters.datasets,include_if_available,important,Datasets used for training
description,$.components[0].description,include_if_available,important,Description of the AI model
domain,$.metadata.properties,include_if_available,supplementary,Domain or field of application
downloadLocation,$.component.externalReferences,include_if_available,important,URL to download the model
embedding_length,$.components[0].properties,include_if_available,supplementary,Vector length of the token embeddings
energyConsumption,$.metadata.properties,include_if_available,important,Energy consumption information
energyQuantity,$.metadata.properties,include_if_available,supplementary,Quantitative energy consumption data
energyUnit,$.metadata.properties,include_if_available,supplementary,Unit of measurement for energy consumption
ethicalConsiderations,$.component.modelCard.considerations.ethicalConsiderations,include_if_available,important,Ethical considerations and fairness assessments
external_references,$.component.externalReferences,include_if_available,supplementary,Additional external references
feed_forward_length,$.components[0].properties,include_if_available,supplementary,Dimensionality of the feed-forward network
hyperparameter,$.metadata.properties,include_if_available,important,Key hyperparameters of the model architecture
informationAboutApplication,$.metadata.properties,include_if_available,supplementary,Information about intended applications
informationAboutTraining,$.metadata.properties,include_if_available,supplementary,Information about the training process
intendedUse,$.component.modelCard.considerations.useCases,include_if_available,important,Intended use cases for the model
licenses,$.components[0].licenses,include_if_available,important,License information for the model
metric,$.metadata.properties,include_if_available,supplementary,Performance metrics and evaluation results
metricDecisionThreshold,$.metadata.properties,include_if_available,supplementary,Decision thresholds for metrics
modelDataPreprocessing,$.metadata.properties,include_if_available,supplementary,Data preprocessing information
modelExplainability,$.metadata.properties,include_if_available,supplementary,Information about model explainability
model_file_size,$.components[0].properties,include_if_available,supplementary,Total size in bytes of the model weight files in the repository
name,$.components[0].name,always_include,critical,Name of the AI model component
paper,none,include_if_present,supplementary,Research paper associated with the model
primaryPurpose,$.component.modelCard.modelParameters.task,include_if_available,critical,Primary purpose or task of the AI model
purl,$.components[0].purl,include_if_available,important,Package URL identifier
quantization_file_type,$.components[0].properties,include_if_available,supplementary,Enum or integer identifier for the quantization bit-precision (e.g. Q4_K_M)
quantization_version,$.components[0].properties,include_if_available,supplementary,Version or specification identifier of the quantization format
rope_dimension_count,$.components[0].properties,include_if_available,supplementary,Number of dimensions for Rotary Position Embedding (RoPE)
runtime_requirement,$.components[0].properties,include_if_available,supplementary,Runtime needed to execute the model based on its weight format: 'python' (loadable from a Python ML stack such as transformers/PyTorch) or 'llm-stack' (requires an LLM inference stack such as llama.cpp/Ollama for GGUF/GGML weights)
safetyRiskAssessment,$.metadata.properties,include_if_available,important,Safety and risk assessment information
serialNumber,$.serialNumber,always_include,critical,Unique identifier for this SBOM instance
specVersion,$.specVersion,always_include,critical,CycloneDX specification version
standardCompliance,$.metadata.properties,include_if_available,supplementary,Standards or regulations the model complies with
suppliedBy,$.component.supplier,include_if_available,critical,Organization or individual that supplied the model
technicalLimitations,$.component.modelCard.considerations.technicalLimitations,include_if_available,important,Known limitations of the model
tokenizer_class,$.components[0].properties,include_if_available,supplementary,The specific tokenizer class or method used
type,$.components[0].type,always_include,critical,Type of component (machine-learning-model)
typeOfModel,$.components[0].modelCard.modelParameters.modelArchitecture,include_if_available,important,Type or architecture of the model
useSensitivePersonalInformation,$.metadata.properties,include_if_available,supplementary,Information about use of sensitive personal data
vcs,none,include_if_present,supplementary,Version control system URL
version,$.version,always_include,critical,Version of this SBOM document
vocab_size,$.components[0].properties,include_if_available,supplementary,Expected size of the model's vocabulary
website,none,include_if_present,supplementary,Model website or documentation URL
35 changes: 30 additions & 5 deletions data_validation/reference_data/update_reference_data.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
"""
Regenerates the lookup tables that track an external release: MITRE ATLAS,
CWE, the SPDX License List and the IANA Language Subtag Registry.
CWE, the SPDX License List, the IANA Language Subtag Registry and the OWASP
AIBOM Generator field registry.

python update_reference_data.py # all of them
python update_reference_data.py --atlas # mitre_atlas_techniques.csv
python update_reference_data.py --cwe # cwe_ids.csv
python update_reference_data.py --spdx # spdx_licenses.csv
python update_reference_data.py --languages # language_subtags.csv
python update_reference_data.py --aibom # aibom_fields.csv

Run it when one of them publishes a new release (the reference-data-refresh
workflow does so monthly), and commit the regenerated CSVs together with the
Expand All @@ -20,6 +22,7 @@
import csv
import io
import json
import os
import re
import sys
import urllib.request
Expand All @@ -42,11 +45,18 @@
CWE_URL = "https://cwe.mitre.org/data/xml/cwec_latest.xml.zip"
SPDX_URL = "https://raw.githubusercontent.com/spdx/license-list-data/main/json/licenses.json"
IANA_SUBTAGS_URL = "https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry"
AIBOM_REPO = "GenAI-Security-Project/aibom-generator"
AIBOM_HEAD_URL = f"https://api.github.com/repos/{AIBOM_REPO}/commits/main"
AIBOM_REGISTRY_URL = f"https://raw.githubusercontent.com/{AIBOM_REPO}/%s/src/models/field_registry.json"
SOURCES = HERE / "SOURCES.md"


def fetch(url: str) -> bytes:
with urllib.request.urlopen(url, timeout=60) as resp: # noqa: S310 - fixed https URLs
request = urllib.request.Request(url) # noqa: S310 - fixed https URLs
token = os.environ.get("GITHUB_TOKEN")
if token and url.startswith("https://api.github.com/"):
request.add_header("Authorization", f"Bearer {token}")
with urllib.request.urlopen(request, timeout=60) as resp: # noqa: S310 - fixed https URLs
return resp.read()


Expand Down Expand Up @@ -156,13 +166,28 @@ def update_languages() -> None:
print(f"language_subtags.csv: {len(rows)} subtags (IANA registry {file_date})")


def update_aibom() -> None:
"""The fields the OWASP AIBOM Generator can write, from its field_registry.json at the current main commit."""
commit = json.loads(fetch(AIBOM_HEAD_URL))["sha"]
fields = json.loads(fetch(AIBOM_REGISTRY_URL % commit))["fields"]
rows = [{"field_id": fid, "cyclonedx_path": f["aibom_generation"]["location"], "rule": f["aibom_generation"]["rule"],
"tier": f["tier"], "description": f["description"]} for fid, f in sorted(fields.items())]
with open(HERE / "aibom_fields.csv", "w", newline="", encoding="utf-8") as fh:
w = csv.DictWriter(fh, fieldnames=["field_id", "cyclonedx_path", "rule", "tier", "description"], lineterminator="\n")
w.writeheader()
w.writerows(rows)
set_version("AIBOM Generator fields", f"aibom-generator@{commit[:12]}")
print(f"aibom_fields.csv: {len(rows)} fields (aibom-generator {commit[:12]})")


def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Regenerate the externally maintained reference tables")
for name in ("atlas", "cwe", "spdx", "languages"):
for name in ("atlas", "cwe", "spdx", "languages", "aibom"):
parser.add_argument(f"--{name}", action="store_true")
args = parser.parse_args(argv)
every = not (args.atlas or args.cwe or args.spdx or args.languages)
for name, update in (("atlas", update_atlas), ("cwe", update_cwe), ("spdx", update_spdx), ("languages", update_languages)):
every = not (args.atlas or args.cwe or args.spdx or args.languages or args.aibom)
for name, update in (("atlas", update_atlas), ("cwe", update_cwe), ("spdx", update_spdx), ("languages", update_languages),
("aibom", update_aibom)):
if every or getattr(args, name):
update()
return 0
Expand Down
4 changes: 4 additions & 0 deletions data_validation/requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Hash-pinned lock generated from requirements-dev.in by data_validation/update_locks.py
# (uv pip compile --universal --generate-hashes --python-version 3.11).
# Do not edit by hand: change requirements-dev.in and rerun the script.
agent-evidence-vectors==0.24.0 \
--hash=sha256:bc8c5785dc9768676361151a4ca2fb4dc1d9bd5a22a67381f6db7adda014539d \
--hash=sha256:fa1c4b804f700a0f71f1a1776978546d9df571b4e5a451b58220478f75e3b6d3
# via -r requirements.in
arrow==1.4.0 \
--hash=sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205 \
--hash=sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7
Expand Down
4 changes: 4 additions & 0 deletions data_validation/requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@ jsonschema[format-nongpl]>=4.18.0
pytest>=7.0.0
# Only for reference_data/update_reference_data.py (refreshing the ATLAS table)
pyyaml>=6.0
# Signed conformance records and their reference verifier, replayed by
# validators/evidence_mapping_check.py for mapping rows that cite test_records.
# Exact pin: a new corpus release can change a verdict, so Dependabot proposes each bump.
agent-evidence-vectors==0.24.0
4 changes: 4 additions & 0 deletions data_validation/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Hash-pinned lock generated from requirements.in by data_validation/update_locks.py
# (uv pip compile --universal --generate-hashes --python-version 3.11).
# Do not edit by hand: change requirements.in and rerun the script.
agent-evidence-vectors==0.24.0 \
--hash=sha256:bc8c5785dc9768676361151a4ca2fb4dc1d9bd5a22a67381f6db7adda014539d \
--hash=sha256:fa1c4b804f700a0f71f1a1776978546d9df571b4e5a451b58220478f75e3b6d3
# via -r requirements.in
arrow==1.4.0 \
--hash=sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205 \
--hash=sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7
Expand Down
14 changes: 11 additions & 3 deletions data_validation/run_all_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
2. Shared checks (data_validation/validators/) on every data file:
schema (for files no dataset validator covers), DSGAI IDs, CVE/CWE/ATLAS
OWASP Top 10 and NIST AI 100-2 IDs, CVSS scores and ratings, dates,
SPDX licences and BCP 47 language tags, anonymization patterns and
duplicates. Errors fail the run;
SPDX licences and BCP 47 language tags, mapping rows that cite
published test records, mapping rows keyed on AIBOM Generator field ids,
anonymization patterns and duplicates. Errors fail the run;
heuristic warnings (anonymization, near-duplicates, retired IDs) are
counted, and printed with --verbose.

Expand All @@ -36,11 +37,13 @@
sys.path.insert(0, str(Path(__file__).resolve().parent))

from validators import ( # noqa: E402
aibom_field_check,
anonymization_scanner,
crossref_validator,
date_check,
dedup_checker,
dsgai_mapping_check,
evidence_mapping_check,
metadata_check,
schema_validator,
severity_check,
Expand Down Expand Up @@ -158,6 +161,8 @@ def shared_checks(dataset: Path, datasets_root: Path) -> tuple[int, list[Finding
findings += severity_check.check_data(path, data)
findings += date_check.check_data(path, data)
findings += metadata_check.check_data(path, data)
findings += evidence_mapping_check.check_data(path, data)
findings += aibom_field_check.check_data(path, data)
findings += anonymization_scanner.check_data(path, data)
records, load_errors = dedup_checker.load_records(files)
findings += load_errors + dedup_checker.check_records(records)
Expand Down Expand Up @@ -225,7 +230,10 @@ def main(argv: list[str] | None = None) -> int:

checked_files = errors = warnings = 0
if not args.no_shared:
print("\nShared checks (schema, DSGAI, CVE/CWE/ATLAS/OWASP/NIST IDs, CVSS, dates, licences, languages, anonymization, duplicates):")
print(
"\nShared checks (schema, DSGAI, CVE/CWE/ATLAS/OWASP/NIST IDs, CVSS, dates, licences, languages,"
" test records, anonymization, duplicates):"
)
for dataset in datasets:
n, findings = shared_checks(dataset, datasets_root)
if not n:
Expand Down
Loading
Loading