From 1a765854ec0f166eb4a4fa4b4c067ab6342beca0 Mon Sep 17 00:00:00 2001 From: anti Date: Thu, 18 Jun 2026 19:02:58 -0400 Subject: [PATCH] fix(1.2): relocate ATT&CK bundle to decnet/data/, bump 19.0 -> 19.1 Bundle pointer moved from repo root to decnet/data/ (with LICENSE.txt), gitignored + fetched on demand (51MB, MITRE-licensed). Version pin bumped 19.0->19.1 with the new sha256; license unchanged. All _REPO_BUNDLE test constants repointed. Fixes test-web failures after the repo-root bundle was deleted. --- CHANGELOG.md | 6 ++++++ decnet/data/.gitignore | 7 +++++++ decnet/ttp/attack_version.py | 6 +++--- tests/ttp/test_attack_bundle_validation.py | 2 +- tests/ttp/test_attack_catalog.py | 2 +- tests/ttp/test_attack_license.py | 2 +- tests/ttp/test_attack_url.py | 2 +- tests/ttp/test_emit_attaches_mitre_url.py | 2 +- tests/ttp/test_groups_for_technique.py | 2 +- tests/ttp/test_intel_mappings.py | 2 +- tests/web/test_api_export_attacker_misp.py | 2 +- tests/web/test_api_export_attacker_stix.py | 4 ++-- tests/web/test_api_export_attackers_misp.py | 2 +- tests/web/test_api_export_attackers_stix.py | 2 +- 14 files changed, 28 insertions(+), 15 deletions(-) create mode 100644 decnet/data/.gitignore diff --git a/CHANGELOG.md b/CHANGELOG.md index eae81ddf..47ff23b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,12 @@ workers the in-process supervisor can't co-host. `gc.freeze()` unnecessary thanks to PEP 683 immortal objects). Not yet wired to a command — the target worker set lands next. +### Changed +- MITRE ATT&CK Enterprise bundle pinned 19.0 → **19.1**. The bundle and its + LICENSE now resolve from `decnet/data/` (hash-pinned in `attack_version.py`, + fetched on demand via `python -m decnet.ttp.attack_stix fetch`, gitignored — + not committed). + ## [1.1.1] - 2026-06-18 ### Fixed diff --git a/decnet/data/.gitignore b/decnet/data/.gitignore new file mode 100644 index 00000000..0da15350 --- /dev/null +++ b/decnet/data/.gitignore @@ -0,0 +1,7 @@ +# MITRE ATT&CK STIX bundle + license live here but are NOT committed: +# ~51MB and MITRE-licensed (fetched on demand, hash-pinned in attack_version.py). +# +# Populate locally / in CI with: +# DECNET_ATTACK_CACHE_DIR=decnet/data python -m decnet.ttp.attack_stix fetch +/enterprise-attack-*.json +/LICENSE.txt diff --git a/decnet/ttp/attack_version.py b/decnet/ttp/attack_version.py index 323f595a..c9854d94 100644 --- a/decnet/ttp/attack_version.py +++ b/decnet/ttp/attack_version.py @@ -16,12 +16,12 @@ from __future__ import annotations from typing import Final -ATTACK_BUNDLE_VERSION: Final[str] = "19.0" +ATTACK_BUNDLE_VERSION: Final[str] = "19.1" -# sha256 of the canonical MITRE-published enterprise-attack-19.0.json +# sha256 of the canonical MITRE-published enterprise-attack-19.1.json # from https://github.com/mitre-attack/attack-stix-data. ATTACK_BUNDLE_SHA256: Final[str] = ( - "df520ea0775a57db7bff760145b02fed89290802913e056b7ed5970b02f3626a" + "bdf1ce86a4e604214c5076d37ae4dcb322678afc528df8492e6fdc1b554f5da3" ) # Raw download URL for the pinned version. diff --git a/tests/ttp/test_attack_bundle_validation.py b/tests/ttp/test_attack_bundle_validation.py index 93cdb1ae..7bdf02e5 100644 --- a/tests/ttp/test_attack_bundle_validation.py +++ b/tests/ttp/test_attack_bundle_validation.py @@ -20,7 +20,7 @@ from decnet.clustering import ukc from decnet.ttp import attack_stix from decnet.ttp.impl import intel_lifter -_REPO_BUNDLE = Path(__file__).resolve().parents[2] / "enterprise-attack-19.0.json" +_REPO_BUNDLE = Path(__file__).resolve().parents[2] / "decnet" / "data" / "enterprise-attack-19.1.json" @pytest.fixture(autouse=True) diff --git a/tests/ttp/test_attack_catalog.py b/tests/ttp/test_attack_catalog.py index 5f48cbcb..557db736 100644 --- a/tests/ttp/test_attack_catalog.py +++ b/tests/ttp/test_attack_catalog.py @@ -19,7 +19,7 @@ from decnet.ttp import attack_stix from decnet.ttp.attack_catalog import technique_name _RULES_DIR = Path(__file__).resolve().parents[2] / "rules" / "ttp" -_REPO_BUNDLE = Path(__file__).resolve().parents[2] / "enterprise-attack-19.0.json" +_REPO_BUNDLE = Path(__file__).resolve().parents[2] / "decnet" / "data" / "enterprise-attack-19.1.json" @pytest.fixture(scope="module", autouse=True) diff --git a/tests/ttp/test_attack_license.py b/tests/ttp/test_attack_license.py index 881b1747..9cbc2804 100644 --- a/tests/ttp/test_attack_license.py +++ b/tests/ttp/test_attack_license.py @@ -19,7 +19,7 @@ from decnet.ttp.attack_version import ( ATTACK_LICENSE_SHA256, ) -_REPO_BUNDLE = Path(__file__).resolve().parents[2] / "enterprise-attack-19.0.json" +_REPO_BUNDLE = Path(__file__).resolve().parents[2] / "decnet" / "data" / "enterprise-attack-19.1.json" @pytest.fixture(autouse=True) diff --git a/tests/ttp/test_attack_url.py b/tests/ttp/test_attack_url.py index 0e25ee99..5553de3a 100644 --- a/tests/ttp/test_attack_url.py +++ b/tests/ttp/test_attack_url.py @@ -15,7 +15,7 @@ import pytest from decnet.ttp import attack_stix -_REPO_BUNDLE = Path(__file__).resolve().parents[2] / "enterprise-attack-19.0.json" +_REPO_BUNDLE = Path(__file__).resolve().parents[2] / "decnet" / "data" / "enterprise-attack-19.1.json" @pytest.fixture(autouse=True) diff --git a/tests/ttp/test_emit_attaches_mitre_url.py b/tests/ttp/test_emit_attaches_mitre_url.py index 50866fd4..6e69667b 100644 --- a/tests/ttp/test_emit_attaches_mitre_url.py +++ b/tests/ttp/test_emit_attaches_mitre_url.py @@ -24,7 +24,7 @@ from decnet.ttp.impl._emit import emit_tags from decnet.ttp.impl.rule_engine import CompiledRule from decnet.ttp.store.base import RuleState -_REPO_BUNDLE = Path(__file__).resolve().parents[2] / "enterprise-attack-19.0.json" +_REPO_BUNDLE = Path(__file__).resolve().parents[2] / "decnet" / "data" / "enterprise-attack-19.1.json" @pytest.fixture(autouse=True) diff --git a/tests/ttp/test_groups_for_technique.py b/tests/ttp/test_groups_for_technique.py index 4012fe5c..f6414a87 100644 --- a/tests/ttp/test_groups_for_technique.py +++ b/tests/ttp/test_groups_for_technique.py @@ -19,7 +19,7 @@ from decnet.web.router.ttp.api_get_groups_for_technique import ( api_groups_for_technique, ) -_REPO_BUNDLE = Path(__file__).resolve().parents[2] / "enterprise-attack-19.0.json" +_REPO_BUNDLE = Path(__file__).resolve().parents[2] / "decnet" / "data" / "enterprise-attack-19.1.json" @pytest.fixture(autouse=True) diff --git a/tests/ttp/test_intel_mappings.py b/tests/ttp/test_intel_mappings.py index 8f61b0ed..d4f7df14 100644 --- a/tests/ttp/test_intel_mappings.py +++ b/tests/ttp/test_intel_mappings.py @@ -31,7 +31,7 @@ from decnet.ttp.data.intel_loader import ( load_provider_mapping, ) -_REPO_BUNDLE = Path(__file__).resolve().parents[2] / "enterprise-attack-19.0.json" +_REPO_BUNDLE = Path(__file__).resolve().parents[2] / "decnet" / "data" / "enterprise-attack-19.1.json" _DATA_DIR = Path(__file__).resolve().parents[2] / "decnet" / "ttp" / "data" / "intel" diff --git a/tests/web/test_api_export_attacker_misp.py b/tests/web/test_api_export_attacker_misp.py index 3180d212..3118a764 100644 --- a/tests/web/test_api_export_attacker_misp.py +++ b/tests/web/test_api_export_attacker_misp.py @@ -14,7 +14,7 @@ from decnet.web.router.attackers.api_export_attacker_misp import ( api_export_attacker_misp, ) -_REPO_BUNDLE = Path(__file__).resolve().parents[2] / "enterprise-attack-19.0.json" +_REPO_BUNDLE = Path(__file__).resolve().parents[2] / "decnet" / "data" / "enterprise-attack-19.1.json" _FAKE_USER: dict = {"uuid": "test-user", "role": "viewer"} diff --git a/tests/web/test_api_export_attacker_stix.py b/tests/web/test_api_export_attacker_stix.py index d68816a2..50f87efa 100644 --- a/tests/web/test_api_export_attacker_stix.py +++ b/tests/web/test_api_export_attacker_stix.py @@ -2,7 +2,7 @@ """Tests for GET /api/v1/attackers/{uuid}/export/stix. Tests call the handler directly (no TestClient). The attack_stix bundle -is pinned to the repo's enterprise-attack-19.0.json so Sighting and +is pinned to the repo's decnet/data/enterprise-attack-19.1.json so Sighting and Relationship target_refs are real MITRE STIX IDs. """ from __future__ import annotations @@ -20,7 +20,7 @@ from decnet.web.router.attackers.api_export_attacker_stix import ( api_export_attacker_stix, ) -_REPO_BUNDLE = Path(__file__).resolve().parents[2] / "enterprise-attack-19.0.json" +_REPO_BUNDLE = Path(__file__).resolve().parents[2] / "decnet" / "data" / "enterprise-attack-19.1.json" _FAKE_USER: dict = {"uuid": "test-user", "role": "viewer"} diff --git a/tests/web/test_api_export_attackers_misp.py b/tests/web/test_api_export_attackers_misp.py index 7f0677ed..7a1c928c 100644 --- a/tests/web/test_api_export_attackers_misp.py +++ b/tests/web/test_api_export_attackers_misp.py @@ -14,7 +14,7 @@ from decnet.web.router.attackers.api_export_attackers_misp import ( api_export_attackers_misp, ) -_REPO_BUNDLE = Path(__file__).resolve().parents[2] / "enterprise-attack-19.0.json" +_REPO_BUNDLE = Path(__file__).resolve().parents[2] / "decnet" / "data" / "enterprise-attack-19.1.json" _FAKE_USER: dict = {"uuid": "test-user", "role": "viewer"} diff --git a/tests/web/test_api_export_attackers_stix.py b/tests/web/test_api_export_attackers_stix.py index 2341a908..5c03d7f1 100644 --- a/tests/web/test_api_export_attackers_stix.py +++ b/tests/web/test_api_export_attackers_stix.py @@ -15,7 +15,7 @@ from decnet.web.router.attackers.api_export_attackers_stix import ( api_export_attackers_stix, ) -_REPO_BUNDLE = Path(__file__).resolve().parents[2] / "enterprise-attack-19.0.json" +_REPO_BUNDLE = Path(__file__).resolve().parents[2] / "decnet" / "data" / "enterprise-attack-19.1.json" _FAKE_USER: dict = {"uuid": "test-user", "role": "viewer"}