Skip to content
Open
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
13 changes: 13 additions & 0 deletions scenarios/misp_lab/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@ Source of truth : `manifest/scenario_vms.json`.

Project convention : last 3 digits of VMID match the IP last octet (1180 -> .180, 1187 -> .187).

## Narrative world

`manifest/world.json` binds the two catalog-provisioned MISP training events to the neutral
Nacre world from MISP's Synthetic Exercise World Format. It records stable MISP UUIDs for
each storyline's country, victim company, and threat actor, plus the exact upstream cluster
commit and SHA-256 used by `range42-catalog`.

This manifest is narrative metadata only. It is deliberately separate from
`scenario_vms.json` and does not affect inventory, VMIDs, networking, feature flags, or the
Ansible deployment path. Consumers should resolve entities by UUID and treat `value` as a
display snapshot.

## Feature flags

See `manifest/feature_flags.yml`. All flags default to `NO`.
Expand Down Expand Up @@ -84,6 +96,7 @@ misp_lab/
manifest/
scenario_vms.json source of truth for VMID / IP / bridge
feature_flags.yml INSTALL_WAZUH (default NO) + INSTALL_TAILSCALE
world.json Nacre + storyline entity UUID bindings (narrative only)
README.md
6 wrapper scripts (see table above)
01_templates-bootstrap/ Ubuntu noble cloud-init image + template 9232
Expand Down
51 changes: 51 additions & 0 deletions scenarios/misp_lab/manifest/world.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"schema_version": "1.0",
"world": {
"id": "nacre",
"format": "misp-galaxy",
"galaxy_type": "exercise-world",
"galaxy_uuid": "3c3de5f0-5982-4c7f-88cf-8abf43b8d6c1",
"collection_uuid": "7d6d7f2f-b3d4-4bc5-9f27-43e12f7f4658",
"version": 1,
"cluster_commit": "cc7792ad2406700bcdd0462927c9fd0f5ddee84a",
"cluster_sha256": "55afe65a817c167a46dbd26850515aefb2bf72b9318f651d6adb172e498a44b4"
},
"storylines": [
{
"id": "spearphishing-initial-access",
"misp_event_uuid": "c0ffee01-cafe-4bab-b000-000000000001",
"entities": {
"country": {
"uuid": "cb97ba71-e477-4acb-9470-10f3e6c9e1a9",
"value": "Asterin Union"
},
"victim_company": {
"uuid": "e3d6ab68-ad7b-4deb-9c0d-c0250013d1bb",
"value": "NovaCore Systems"
},
"threat_actor": {
"uuid": "6c922d71-43e3-40c4-a1ea-85929f0c2f1a",
"value": "TA-700 Obsidian Jackal"
}
}
},
{
"id": "ransomware-c2-exfiltration",
"misp_event_uuid": "c0ffee02-cafe-4bab-b000-000000000002",
"entities": {
"country": {
"uuid": "e4cd09cc-7be7-46ef-8989-5b9a812b49fe",
"value": "Velkar Republic"
},
"victim_company": {
"uuid": "df601e63-1a5f-4cec-a071-152f1c62f310",
"value": "HelixCore Group"
},
"threat_actor": {
"uuid": "e8b1ce9a-128c-4546-907d-02178fc72e49",
"value": "TA-701 Silver Mantis"
}
}
}
]
}
55 changes: 55 additions & 0 deletions tests/test_misp_lab_world_manifest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import json
import unittest
from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]
MANIFEST = ROOT / "scenarios" / "misp_lab" / "manifest" / "world.json"


class MispLabWorldManifestTests(unittest.TestCase):
def test_misp_lab_binds_both_training_storylines_to_nacre_uuids(self) -> None:
self.assertTrue(MANIFEST.is_file(), "misp_lab is missing manifest/world.json")

with MANIFEST.open() as handle:
document = json.load(handle)

self.assertEqual("1.0", document["schema_version"])
self.assertEqual(
{
"id": "nacre",
"format": "misp-galaxy",
"galaxy_type": "exercise-world",
"galaxy_uuid": "3c3de5f0-5982-4c7f-88cf-8abf43b8d6c1",
"collection_uuid": "7d6d7f2f-b3d4-4bc5-9f27-43e12f7f4658",
"version": 1,
"cluster_commit": "cc7792ad2406700bcdd0462927c9fd0f5ddee84a",
"cluster_sha256": "55afe65a817c167a46dbd26850515aefb2bf72b9318f651d6adb172e498a44b4",
},
document["world"],
)

expected = {
"c0ffee01-cafe-4bab-b000-000000000001": {
"country": ("cb97ba71-e477-4acb-9470-10f3e6c9e1a9", "Asterin Union"),
"victim_company": ("e3d6ab68-ad7b-4deb-9c0d-c0250013d1bb", "NovaCore Systems"),
"threat_actor": ("6c922d71-43e3-40c4-a1ea-85929f0c2f1a", "TA-700 Obsidian Jackal"),
},
"c0ffee02-cafe-4bab-b000-000000000002": {
"country": ("e4cd09cc-7be7-46ef-8989-5b9a812b49fe", "Velkar Republic"),
"victim_company": ("df601e63-1a5f-4cec-a071-152f1c62f310", "HelixCore Group"),
"threat_actor": ("e8b1ce9a-128c-4546-907d-02178fc72e49", "TA-701 Silver Mantis"),
},
}
actual = {
storyline["misp_event_uuid"]: {
role: (entity["uuid"], entity["value"])
for role, entity in storyline["entities"].items()
}
for storyline in document["storylines"]
}
self.assertEqual(expected, actual)


if __name__ == "__main__":
unittest.main()