Files
DECNET/tests/api/ttp/conftest.py
anti 42e9492118 feat(ttp): inspector drawer surfaces evidence + rule_id behind each technique
The TTPsObservedSection rollup tells the operator "we saw T1059" but
not why. Click any technique row → side drawer opens listing every
ttp_tag row in scope with the persisted evidence JSON, firing
rule_id / rule_version, source_kind / source_id, confidence, and
created_at. Mirrors the CredentialReuseInspector / BountyInspector
pattern (drawer-backdrop + bd-head/bd-body + kvs grid).

Backend:
- New `GET /api/v1/ttp/tags/by-{scope}/{uuid}/{technique_id}`
  (`scope ∈ {identity, attacker, session}`, optional
  `?sub_technique_id=`, `?limit=` capped to 1000). Returns raw
  TTPTag rows newest-first.
- New `TTPTagDetailRow` Pydantic model + re-export.
- New repo method `list_tags_by_scope_and_technique` on
  TTPMixin (+ abstract on BaseRepository) — single query branched
  on scope; identity scope projects through `Attacker.identity_id`
  the same way `list_techniques_by_identity` does.
- Tests: evidence round-trips, sub_technique filter, JWT-required,
  empty scope, unknown scope rejected.

Frontend:
- New `TTPInspector.tsx` + `TTPInspector.css` (violet accent, slide
  animation, focus-trapped panel matching the existing inspector
  family).
- `TTPsObservedSection`'s TechniqueBar is now click+keyboard
  activatable; clicking opens the inspector for that
  (technique, sub_technique) tuple.

mypy clean. 532 passed in the targeted sweep.
2026-05-02 02:55:05 -04:00

34 lines
1.3 KiB
Python

"""Shared helpers for TTP API contract tests (E.2.8).
The base ``tests/api/conftest.py`` already provides ``client``,
``auth_token`` (admin role) and ``viewer_token`` (viewer role). This
module adds TTP-specific path constants + a small ``_hdr`` helper so
each test file stays focused on the one endpoint it covers.
"""
from __future__ import annotations
_BASE = "/api/v1/ttp"
def hdr(token: str) -> dict[str, str]:
return {"Authorization": f"Bearer {token}"}
# ─── Endpoint paths ──────────────────────────────────────────────────────────
# Read endpoints — every entry must round-trip 401 without a JWT and
# 200 with one. Documented in TTP_TAGGING.md "API surface".
TECHNIQUES = f"{_BASE}/techniques"
BY_IDENTITY = _BASE + "/by-identity/{identity_uuid}"
BY_ATTACKER = _BASE + "/by-attacker/{attacker_uuid}"
BY_CAMPAIGN = _BASE + "/by-campaign/{campaign_uuid}"
BY_SESSION = _BASE + "/by-session/{session_id}"
RULES = f"{_BASE}/rules"
TAG_DETAILS = _BASE + "/tags/by-{scope}/{uuid}/{technique_id}"
NAVIGATOR = f"{_BASE}/export/navigator"
NAVIGATOR_IDENTITY = _BASE + "/export/navigator/identity/{uuid}"
# Mutation endpoints — admin-only.
RULE_STATE = _BASE + "/rules/{rule_id}/state"