Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ dev = [
# GPL, and over the [format-nongpl] extra, which pulls in thirteen
# distributions to reach the same two checkers.
"rfc3986-validator>=0.1.1",
# Registers the "date-time" format checker used by AVE record metadata.
"rfc3339-validator>=0.1.4",
]

# Not a Python library -- nothing here imports "bawbel_ave" as a package.
Expand All @@ -30,4 +32,4 @@ dev = [
packages = []

[tool.pytest.ini_options]
testpaths = ["tests"]
testpaths = ["tests"]
19 changes: 13 additions & 6 deletions scripts/validate_records.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@
# consumer of the corpus. A stray vendor product name is a neutrality
# violation this project enforces everywhere else; records shouldn't be
# the one place it's unchecked.
# How: jsonschema.Draft202012Validator against schema/ave-record-1.1.0.schema.json
# (handles the draft-vs-active conditional required set natively), plus a
# handful of checks the schema's additionalProperties:false already implies
# but which deserve a readable, named failure message of their own
# How: jsonschema.Draft202012Validator with format checking enabled against
# schema/ave-record-1.1.0.schema.json (handles the draft-vs-active
# conditional required set natively and enforces date-time / uri metadata),
# plus a handful of checks the schema's additionalProperties:false already
# implies but which deserve a readable, named failure message of their own
import json
import re
import sys
Expand Down Expand Up @@ -55,6 +56,12 @@ def check_schema(record: dict, validator: jsonschema.Draft202012Validator) -> li
for e in validator.iter_errors(record)]


def build_validator(schema: dict) -> jsonschema.Draft202012Validator:
return jsonschema.Draft202012Validator(
schema, format_checker=jsonschema.Draft202012Validator.FORMAT_CHECKER
)


def check_no_old_field_names(record: dict) -> list[str]:
return [f"stale field name '{f}' still present (renamed in v1.1.0)"
for f in OLD_FIELD_NAMES if f in record]
Expand Down Expand Up @@ -136,7 +143,7 @@ def check_no_vendor_boilerplate(raw_text: str) -> list[str]:
def main() -> int:
schema = json.loads(SCHEMA_PATH.read_text())
jsonschema.Draft202012Validator.check_schema(schema)
validator = jsonschema.Draft202012Validator(schema)
validator = build_validator(schema)

paths = sorted(RECORDS_DIR.glob("AVE-*.json"))
if not paths:
Expand Down Expand Up @@ -169,4 +176,4 @@ def main() -> int:


if __name__ == "__main__":
raise SystemExit(main())
raise SystemExit(main())
23 changes: 23 additions & 0 deletions tests/test_validate_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import json

from scripts import validate_records


def test_record_validator_rejects_invalid_date_time_format():
schema = json.loads(validate_records.SCHEMA_PATH.read_text(encoding="utf-8"))
validator = validate_records.build_validator(schema)
record = {
"ave_id": "AVE-2026-99999",
"schema_version": "1.1.0",
"status": "draft",
"title": "Invalid date-time fixture",
"description": "A minimal draft record with malformed published metadata.",
"attack_class": "test",
"behavioral_fingerprint": "test",
"references": [{"title": "Example", "url": "https://example.com"}],
"published": "not-a-date-time",
}

errors = validate_records.check_schema(record, validator)

assert any("not-a-date-time" in error for error in errors)