From 9f2e4b681c054eaae68b3edbf05c94ada8a89b09 Mon Sep 17 00:00:00 2001 From: Magnus Hedemark Date: Sun, 26 Jul 2026 18:41:19 -0400 Subject: [PATCH] fix(raleigh): validate included resource identifiers --- raleigh/EVIDENCE-LEDGER.md | 2 +- raleigh/scripts/raleighlib/public_safety_stats.py | 14 ++++++++++++-- raleigh/tests/test_raleigh.py | 9 +++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/raleigh/EVIDENCE-LEDGER.md b/raleigh/EVIDENCE-LEDGER.md index 4f9bbfa..053ee06 100644 --- a/raleigh/EVIDENCE-LEDGER.md +++ b/raleigh/EVIDENCE-LEDGER.md @@ -170,7 +170,7 @@ The following public service boundaries were exercised successfully on 2026-07-2 ### Verification -- `PYTHONDONTWRITEBYTECODE=1 python3 -m unittest raleigh/tests/test_raleigh.py`: **357 tests passed**. +- `PYTHONDONTWRITEBYTECODE=1 python3 -m unittest raleigh/tests/test_raleigh.py`: **358 tests passed**. - `python3 -m ruff check raleigh/scripts/raleighlib/core.py raleigh/scripts/raleighlib/public_safety_stats.py raleigh/scripts/raleighlib/cli.py`: passed. - `python3 scripts/validate-evals.py raleigh`: **11 eval manifests validated**. - `ruby scripts/validate-skills.rb`: **110 canonical skills validated**. diff --git a/raleigh/scripts/raleighlib/public_safety_stats.py b/raleigh/scripts/raleighlib/public_safety_stats.py index 47573e5..5c56a25 100644 --- a/raleigh/scripts/raleighlib/public_safety_stats.py +++ b/raleigh/scripts/raleighlib/public_safety_stats.py @@ -176,14 +176,24 @@ def _fetch_page(agency: str) -> tuple[dict[str, Any], dict[str, str]]: not isinstance(item, dict) or not isinstance(item.get("type"), str) or not isinstance(item.get("id"), str) + or not item["type"] + or not item["id"] ): raise PublishedStatisticsError("published statistics relationship identifiers are invalid") referenced.add((item["type"], item["id"])) fragments: dict[str, str] = {} for item in included: - if not isinstance(item, dict) or item.get("type") != "paragraph--stories_text": + if ( + not isinstance(item, dict) + or not isinstance(item.get("type"), str) + or not isinstance(item.get("id"), str) + or not item["type"] + or not item["id"] + ): + raise PublishedStatisticsError("published statistics included resource identifiers are invalid") + if item["type"] != "paragraph--stories_text": continue - if (item.get("type"), item.get("id")) not in referenced: + if (item["type"], item["id"]) not in referenced: raise PublishedStatisticsError("published statistics included an unreferenced content section") item_attrs = item.get("attributes") if not isinstance(item_attrs, dict) or item_attrs.get("status") is not True: diff --git a/raleigh/tests/test_raleigh.py b/raleigh/tests/test_raleigh.py index 5cd9e11..14aff7d 100644 --- a/raleigh/tests/test_raleigh.py +++ b/raleigh/tests/test_raleigh.py @@ -3094,6 +3094,15 @@ class PublishedPublicSafetyStatisticsTests(unittest.TestCase): with self.assertRaisesRegex(public_safety_stats.PublishedStatisticsError, "identifiers are invalid"): public_safety_stats.reports("police") + def test_malformed_jsonapi_included_resource_fails_visibly(self): + for value in ([], {"type": "paragraph--stories_text", "id": []}): + with self.subTest(value=value): + fixture = self._fixture("police") + fixture["included"][0] = value + with patch("raleighlib.public_safety_stats.core.json_request", return_value=fixture): + with self.assertRaisesRegex(public_safety_stats.PublishedStatisticsError, "included resource identifiers are invalid"): + public_safety_stats.reports("police") + def test_report_link_outside_canonical_origins_fails_closed(self): fixture = self._fixture("police") html = fixture["included"][0]["attributes"]["field_stories_text_formatted"]["value"]