Commit Graph

239 Commits

Author SHA1 Message Date
kitos
b98a539d93 fix(intel-scan): remove duplicate _entry_matches + replace dead NVD feed
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
1. Duplicate function definition: the old 2-param _entry_matches shadowed
   the new 3-param version — Python uses the last definition, so the call
   with 3 args threw TypeError. Removed the stale old definition.

2. NIST NVD deprecated their XML RSS feeds in 2023 — URL returns 404.
   Replaced with SecurityWeek RSS which is active and covers CVEs/threats.
2026-05-29 16:58:07 +02:00
kitos
65c34c3374 fix(rt-import): require Blue Lead validation before coverage counts
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
RT tests are created in 'in_review' state (not validated):
- red_validation_status = 'approved' (RT confirmed execution)
- blue_validation_status = null (pending Blue Lead review)
- detection_result is pre-filled from the import JSON

Blue Lead sees these in their normal validation queue and confirms
or rejects the detection result. Only after Blue Lead approval does
the technique coverage update to validated/not_covered/partial.

This gives Blue Lead oversight over RT findings rather than auto-
accepting external engagement results as ground truth.
2026-05-29 16:21:06 +02:00
kitos
2f1ef7545d feat(rt-import): import Red Team engagement results as validated tests
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Backend — POST /tests/import-rt (red_lead + admin):
  Accepts engagement JSON with name/date/description/operator and
  a list of techniques each with mitre_id, result, attack_success,
  platform, notes. Creates one Test per technique directly in
  'validated' state (red + blue validation = approved) bypassing
  the normal workflow. Recalculates technique.status_global for
  all affected techniques. Returns created/skipped summary.

Frontend — /tests/import-rt (new dedicated page):
  - Format reference panel (collapsible) with field descriptions
  - Download template JSON button (generates a filled example)
  - Paste JSON textarea + file upload (.json)
  - Live validation + preview table showing what will be imported
  - Import button with spinner
  - Success / warning / error result display
  Accessible to admin and red_lead only.
  Added to sidebar under Tests > Import RT Results.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 16:15:35 +02:00
kitos
b39a4fec14 feat(intel): major intel scan improvements + Review Queue integration
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Backend:
- intel_service: remove 50-technique limit (scan all techniques), improve
  pattern matching with word boundaries (\bT1059\b), raise min name length
  to 8 chars to reduce false positives, skip entries with empty titles
- technique_query_service: add intel_items to get_technique_detail() so
  the technique page now shows recent threat intel articles (last 20)
- New GET /intel/items endpoint with optional technique_id filter

Frontend:
- New api/intel.ts with listIntelItems()
- ReviewQueuePage: complete redesign
    * Expandable rows — click a technique to see its intel articles inline
    * IntelPanel component fetches articles per technique on expand
    * 'Create Template from Intel' button opens pre-filled modal:
      name (from article title), source_url (article link), technique_id
      User reads the article and fills the attack procedure
    * Updated explanation text: lists all 3 reasons a technique can be flagged
      (MITRE update / intel scan / new template or detection rule)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 16:04:30 +02:00
kitos
07c6164ceb fix(permissions): hide action buttons for unauthorized roles
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
TestCatalogPage: 'Use Template' button had no role check — any user
(including viewer/blue_tech/red_tech) could see and click it, which
would fail at the backend (POST /tests/from-template requires
red_lead|blue_lead). Added canUseTemplate check; button hidden for
viewer, blue_tech, red_tech.

TechniqueDetailPage: 'Run This Test' / 'Re-run' buttons in the
Available Templates section also had no role check. Added canRunTemplate
(same criteria: admin|red_lead|blue_lead). The 'View test' button for
active tests remains visible to everyone (read-only navigation).

Principle: if a user cannot perform the action, the button does not
appear — no permission error messages, just absence of the control.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 15:47:08 +02:00
kitos
f590a00006 fix(permissions): hide non-actionable UI + fix viewer route access
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
1. /executive-dashboard: add 'viewer' to ProtectedRoute roles — sidebar
   showed the link to viewers but the route redirected them to /dashboard.
2. /comparison: same fix — viewer was in sidebar roles but not in route.
3. /techniques/review-queue: add ProtectedRoute (leads+admin) — the page
   had no route-level protection, any authenticated user could access it.
4. TechniqueDetailPage review banner: hide from users who can't act on it.
   Previously shown to everyone with a 'Leads only' badge; now only shown
   to canReview users (admin/red_lead/blue_lead). Non-leads don't need to
   see alerts about changes they cannot acknowledge.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 15:25:36 +02:00
kitos
8a542f912d feat(threat-actors): infer motivation via curated map + description keywords
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
MITRE ATT&CK STIX data never includes primary_motivation on intrusion-set
objects. Motivation is now derived with a 3-tier fallback:
  1. Curated MITRE-ID override map (100+ known groups mapped by hand)
  2. STIX primary_motivation field (if MITRE ever adds it)
  3. Description keyword inference (financ/ransomware/espionage/
     nation-state/destructive/hacktivist patterns)

Re-running MITRE sync will now backfill motivation for existing actors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 15:13:05 +02:00
kitos
e49eca0b24 fix(threat-actors): fix 500 on search + populate motivation from STIX
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
1. fix(search 500): func.cast(col, func.text()) is invalid SQLAlchemy —
   replaced with cast(col, Text) for both aliases and target_sectors
   JSONB columns. Generating correct CAST(col AS TEXT) SQL.

2. feat(motivation): extract primary_motivation and sophistication from
   STIX intrusion-set objects during MITRE sync. Added _normalize_motivation()
   to map STIX vocabulary → simplified frontend values (espionage / financial /
   destruction / hacktivism). Both create and update paths now set these fields.
   Run MITRE sync to backfill existing actors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 14:09:04 +02:00
kitos
7d856bef43 feat(compliance): add DORA (EU 2022/2554) framework with ATT&CK mappings
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Implements the Digital Operational Resilience Act as a compliance framework
using the same pattern as CIS Controls v8 (hardcoded curated mappings,
no official STIX bundle exists for DORA).

22 controls across 5 chapters mapped to MITRE ATT&CK techniques:
  Ch. II  — ICT Risk Management (Art. 5–15): governance, identification,
            protection, detection, response, backup, threat intel
  Ch. III — Incident Management (Art. 17–19): classification, reporting
  Ch. IV  — Resilience Testing (Art. 24–27): general testing + TLPT
            (Art. 26 explicitly based on TIBER-EU/ATT&CK threat-led testing)
  Ch. V   — Third-Party Risk (Art. 28, 30, 42): supply chain, trusted rels.
  Ch. VI  — Information Sharing (Art. 45)

Technique mappings derived from ENISA DORA guidelines and TIBER-EU framework.
Import is triggered via POST /api/v1/compliance/import/dora (admin only).
Frontend: new 'DORA' button in the Compliance page import section.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 13:52:51 +02:00
kitos
70b5c833d4 fix(tests): move showTemplateModal useState before early returns (React #310)
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
The useState hook was placed after the isLoading/error early returns,
violating the Rules of Hooks. First render hit the early return without
calling the hook; second render (after data loaded) called it, producing
'more hooks than previous render' — React error #310 and a white screen.

Moved const [showTemplateModal] to the state block at the top of the
component, alongside the other useState declarations.
2026-05-29 13:29:17 +02:00
kitos
6c8a1317fd fix(layout): add React error boundary to catch render crashes
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Previously a JS rendering error produced a blank white screen with no
feedback. PageErrorBoundary now catches the error, shows the error
message + stack trace, and offers a reload button. This will surface
the exact crash message for the inaccessible test page.
2026-05-29 13:23:28 +02:00
kitos
9310652944 feat(tests): Save as Template button on test detail page
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Adds a 'Save as Template' button in the Details sidebar (visible to
red_lead, blue_lead and admin only). Opens a modal pre-filled from
the test's own fields:

  test.name           → template name
  test.description    → description
  test.platform       → platform
  test.procedure_text → attack_procedure
  test.tool_used      → tool_suggested
  test.technique_mitre_id → mitre_technique_id

User can also set severity and write expected_detection (Blue Team
guidance — not stored on tests). Calls POST /test-templates with
source='custom' on submit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 12:57:29 +02:00
kitos
193c48d031 feat(sidebar): add Techniques page to menu under ATT&CK group
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
/techniques (technique browser with filters) was an orphaned route —
only reachable via 'Back to techniques' button or direct URL.
Now exposed in the sidebar as part of a new ATT&CK group:
  ATT&CK ▾
    Techniques       → /techniques
    Coverage Matrix  → /matrix
    Review Queue     → /techniques/review-queue (leads+admin only)

Child role filtering added to SidebarLink.
Review Queue badge moved to the ATT&CK group header.
2026-05-29 12:45:59 +02:00
kitos
416b31a5b6 fix(d3fend): add items-start to grid so cards don't stretch to row height
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
CSS grid was stretching both cells in a row to the same height as the
tallest card, making the unexpanded card appear 'open' with blank space.
items-start makes each card only as tall as its own content.
2026-05-29 12:16:56 +02:00
kitos
843b545df3 fix(d3fend): use d3fend_id as expand key instead of def.id
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
def.id was undefined for D3FEND items, causing expandedId===undefined
to match ALL items simultaneously and opening every card at once.
d3fend_id (e.g. 'D3-DSDP') is always present and unique per defense.
2026-05-29 11:58:28 +02:00
kitos
2238ca671b fix(jira): show test Jira tickets on technique page (correct entity model)
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Techniques don't have their own Jira tickets — tickets exist on tests
and campaigns. The previous JiraLinkPanel entityType='technique' always
returned empty.

Backend: add entity_ids (list) filter to GET /jira/links so multiple
  test IDs can be fetched in a single request.
Frontend API: listJiraLinks() accepts entity_ids[] and serialises them
  as repeated query params (required by FastAPI List[UUID] parsing).
TechniqueDetailPage: replace JiraLinkPanel with TechniqueJiraSection —
  a dedicated read-only component that:
  - Takes technique.tests (already loaded)
  - Batch-fetches all test Jira links in one request
  - Shows test name + ticket key + status + priority + open-in-Jira link
  - Hides itself when no tickets exist (avoids empty panel)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 11:48:55 +02:00
kitos
e9aa473a6b fix(techniques): add readOnly to JiraLinkPanel on technique detail page
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
2026-05-29 11:42:08 +02:00
kitos
bd0493aade fix(ui): make all Jira and time panels read-only everywhere
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
WorklogTimeline: add readOnly prop — hides 'Log Time' button and form.
TestPhaseTimeline: remove 'Sync to Tempo' button from TempoSyncBadge;
  only displays the green 'Tempo' badge when already synced. Cleans up
  unused imports (useState, useMutation, useQueryClient, syncTestToTempo).
CampaignDetailPage: JiraLinkPanel and WorklogTimeline both now rendered
  with readOnly=true; JiraLinkPanel receives campaign name as label.

Jira tickets and time worklogs are created automatically by the system
(campaign activation, test workflow) — no manual editing from detail pages.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 11:33:55 +02:00
kitos
d7d11dfdf5 feat(review-queue): trigger review_required on new test templates
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Extends the review queue triggers to cover test template imports:
- atomic_import_service: flags techniques when new Atomic Red Team
  templates are imported
- caldera_import_service: same for Caldera templates
- lolbas_import_service: same for LOLBAS templates
- test_templates router (manual creation): flags the technique when
  an admin/lead creates a custom template via the API

Pattern is identical to the Sigma/Elastic detection rule approach:
collect new mitre_ids during the loop, bulk-update after commit.
Manual creation does a single technique lookup and sets the flag
inside the existing UnitOfWork.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 11:26:09 +02:00
kitos
1b513b050e fix: 4 improvements — campaign test deletion, review queue triggers, technique link, Jira read-only
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
1. Campaign test deletion: removing a test from a campaign now also
   deletes the underlying Test record and recalculates technique status.

2. Review Queue triggers: review_required=True is now also set when
   - Sigma/Elastic detection rules are imported for a technique
   - A test is validated (coverage status changes)

3. Test detail — Technique link: 'Technique' entry added at the top of
   the Details sidebar showing MITRE ID + name as a clickable link to
   /techniques/{mitre_id}.

4. Jira panel — read-only on test page: added readOnly + label props to
   JiraLinkPanel. TestDetailPage now passes readOnly=true and the test
   name as label, hiding Link Issue / Sync / Unlink controls (automatic
   Jira creation only — no manual management).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 11:18:55 +02:00
kitos
727b8af7fd feat(techniques): show test status on template cards
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Each template card in 'Available Test Templates' now shows contextual
status derived from technique.tests (already loaded):

- Active test (draft/executing/evaluating/in_review):
    blue 'Executing / In Review' badge + 'View test →' button
    (prevents blind duplicate creation)
- Validated / detected (fresh):
    green 'Detected' badge + dimmed 'Re-run' button
- Validated / not_detected or partial:
    red/yellow result badge + full 'Run This Test' button (re-run encouraged)
- Validated but stale (review_required=true):
    result badge + '⚠ Coverage may be stale' line
- No tests: normal 'Run This Test' button

No extra API calls — status is derived from the technique detail
already in-memory.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 10:59:39 +02:00
kitos
c467459b51 fix(campaigns): filter existing-test picker to draft + not in any campaign
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Backend: add not_in_any_campaign filter to list_tests (subquery on
CampaignTest) and expose it as a query param on GET /tests.
Frontend: the 'Existing Test' tab now requests only
  state=draft & not_in_any_campaign=true
so tests already linked to any campaign or not in draft state
are never shown.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 09:55:02 +02:00
kitos
b19ecc0d5f feat(campaigns): prefix test names with [Campaign] on add
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- From template: name is pre-filled as '[Campaign] {template.name}'
  (user can edit before confirming).
- Existing test: renamed via PATCH /tests/{id} to prepend '[Campaign] '
  before being linked to the campaign, consistent with the APT-generated
  campaign flow.
  Idempotent — skips rename if the name already starts with '[Campaign]'.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 09:19:07 +02:00
kitos
2910aea6b2 feat(campaigns): add 'From Template' tab in Add Test modal
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
The modal now has two tabs:
- 'From Template' (default): searchable/filterable template catalog
  → select template → customise name/platform/procedure/tool
  → 'Create & Add to Campaign' (two-step: POST /tests/from-template
    then POST /campaigns/{id}/tests)
- 'Existing Test': previous behaviour — add an already-created test

Both tabs share an added-count footer badge.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 09:10:03 +02:00
kitos
20075305a5 feat(review-queue): MITRE update review queue for leads
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- New /techniques/review-queue page: lists all techniques flagged for
  review after a MITRE ATT&CK sync, grouped by tactic. Leads and admins
  can mark each one reviewed inline without leaving the page.
- Sidebar: 'Review Queue' link (admin/red_lead/blue_lead only) with an
  amber badge showing the live pending count.
- TechniqueDetailPage: amber banner when review_required=true explaining
  what happened and who can act; 'Mark as Reviewed' button now amber
  coloured for visual distinction. 'Leads only' chip shown for blue_tech.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 08:58:32 +02:00
kitos
4881825fea fix(techniques): remove broken validate/reject buttons from associated tests
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
The tick/cross buttons navigated to /tests/:id/validate and /tests/:id/reject
which are non-existent routes (catch-all redirected to dashboard).
Removed both buttons; the View (FileText) icon is the correct entry point
to the test detail page where the full workflow lives.
2026-05-29 08:54:31 +02:00
kitos
de093778f6 feat(markdown): extract MITRE citations into collapsible sources section
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
(Citation: ...) patterns are stripped from body text, replaced with
Unicode superscript numbers (¹²³), and shown in a compact "Sources"
section below — collapsed when there are more than 3, expanded otherwise.
Deduplication ensures the same citation reference appears only once.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 08:44:52 +02:00
kitos
34340a67eb fix(frontend): align react-markdown version to ^10.1.0 to match lock file
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
2026-05-29 08:40:22 +02:00
kitos
db208b9f5c feat(frontend): render markdown in description and summary fields
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- New shared MarkdownText component (react-markdown + remark-gfm)
  that renders links, bold, italic, lists, code, blockquotes.
  External links open in a new tab with rel=noopener.
- Applied to: technique description, threat actor description,
  test description, campaign description, detection rule descriptions,
  D3FEND defense descriptions, red/blue summaries and validation notes.
- procedure_text (code/commands) stays in <pre> — not processed as MD.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 08:38:53 +02:00
kitos
a8542512b4 fix(ui+backend): sidebar active state + technique status after test deletion
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- Sidebar: add `end` prop to child NavLinks so "All Tests" (/tests) is
  only highlighted when exactly on /tests, not on /tests/validated.
- Backend: recalculate technique status_global for all affected techniques
  when tests are deleted via delete_campaign(delete_tests=True), preventing
  stale coverage metrics on the dashboard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 17:55:04 +02:00
kitos
1120d8f2ce feat(tests): add Validated Tests as dedicated page, remove duplicate sidebar entry
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- New /tests/validated page with its own route and sidebar link, showing
  only validated tests with Attack and Detection result badges.
- Removed the duplicate "My Pending Tasks" sidebar entry (same as All Tests).
- All Tests table no longer shows validated tests; clicking the Validated
  counter card navigates to the new page instead.
- Validated option removed from the state filter dropdown in All Tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 17:18:21 +02:00
kitos
2eed763f9e feat(tests): separate validated tests section + waiting time column
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- Validated tests no longer appear in the active tests table; they are
  shown in a dedicated collapsible "Validated Tests" section at the bottom
  (with its own sortable table and count badge).
- Added "Waiting" column to the main table showing elapsed time since
  last update for blue_evaluating tests, sortable so Blue Team can
  prioritise the oldest pending evaluations.
- Sorting by Waiting pushes blue_evaluating rows to the top and orders
  them oldest-first by default.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 17:07:16 +02:00
kitos
2865846db2 fix(auth): prevent reuse of current password on first-access change
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
When must_change_password is true the user must pick a genuinely new
password. Added a verify_password check against the existing hash before
accepting the new value, raising BusinessRuleViolation if they match.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 16:56:47 +02:00
kitos
8b035b5c5c fix(compliance): fix broken table layout and expand caused by nested tbody elements
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Rewrote ControlsTable with React fragments instead of nested <tbody> tags,
added ScoreBar component, improved status badges, filter header strip,
and grid layout for expanded technique cards.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 16:45:47 +02:00
kitos
b248c2816e fix(tests): apply user edits when creating test from template
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
The form captured name/description/platform/procedure/tool edits but
never sent them — the created test always used the raw template values.

- TestTemplateInstantiate schema: add optional override fields
  (name, description, platform, procedure_text, tool_used)
- create_test_from_template service: accept *_override kwargs;
  use override value when provided, fall back to template value
- Router: pass all override fields from payload to service
- Frontend API createTestFromTemplate: accept overrides object, spread into body
- TestFromTemplateForm: pass all form state values as overrides

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 16:38:40 +02:00
kitos
fa8e7f311b feat(techniques): show detection rules on technique detail page
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Backend:
- technique_query_service.get_technique_detail() now queries DetectionRule
  by mitre_technique_id == mitre_id (same field the heatmap uses)
- Rules sorted: critical → high → medium → low → informational, then alphabetically
- Returns: id, title, description, source, source_url, rule_format,
  severity, platforms, false_positive_rate

Frontend:
- New DetectionRulesSection component with expandable rows per rule
- Color-coded severity dots and badges (red/orange/yellow/blue/gray)
- Source badges (sigma=purple, elastic=blue, splunk=orange, custom=cyan)
- Shows format, false positive rate, platforms, source link on expand
- Empty state when no rules exist

Fixes: T1189 showed green in heatmap but no rules on detail page

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 16:26:46 +02:00
kitos
2371318e9e fix(heatmap): detection rules layer uses absolute rule count, not relative max
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Before: score = (rules/max_rules)*50 + (evaluated/rules)*50
  -> everything red because relative to the 1 technique with most rules

After: score = min(rules/4 * 100, 100)  — absolute thresholds
  0 rules  = gray  (not covered)
  1 rule   = red   (25 — minimal)
  2 rules  = orange (50 — some)
  3 rules  = yellow (75 — good)
  4+ rules = green  (100 — well covered)

Also update HeatmapLegend labels to show actual rule counts instead of
meaningless percentage ranges.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 16:11:29 +02:00
kitos
8024f32954 feat(dashboard): auto-compute risk scores + refresh button on Critical Gaps
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- Auto-trigger POST /risk/compute on first load if no profiles exist
- Add "Refresh scores" button next to Critical Gaps header (spins while computing)
- Add computeRiskScores() to frontend/src/api/risk.ts
- After compute, invalidate risk-profiles query so table updates immediately

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 15:58:49 +02:00
kitos
45b13bccde feat(dashboard): sort Critical Gaps by risk score instead of MITRE ID
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- Create frontend/src/api/risk.ts with getRiskProfiles() API function
- Executive Dashboard fetches risk profiles and builds a techniqueId→profile map
- Critical Gaps sorted by risk_score DESC (highest risk shown first)
- Ties resolved: not_covered before not_evaluated; unscored techniques last
- Table now shows Risk Score (0-100, color-coded) and Risk Level badge per row
- Column renamed to "Critical Gaps — Top 10 by Risk Priority"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 15:42:52 +02:00
kitos
2e5b47a4a2 feat(campaigns): delete campaign button + defer Jira to Activate
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- Backend: add DELETE /campaigns/{id}?delete_tests=bool endpoint
- Backend: add delete_campaign() service — handles draft-only restriction,
  optional test deletion, nullifies child campaign FKs
- Backend: remove early Jira ticket creation from POST /campaigns,
  POST /campaigns/{id}/tests, and POST /campaigns/from-threat-actor
- Backend: activate endpoint now creates campaign Jira ticket if missing,
  then creates test tickets (all deferred from creation to activation)
- Frontend: add deleteCampaign() API function to campaigns.ts
- Frontend: two-step confirmation dialog on CampaignDetailPage —
  first confirms deletion, then asks whether to also delete associated tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 14:36:25 +02:00
kitos
664210be3d fix(types): add tempo_worklog_id to Worklog interface
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 14:10:58 +02:00
kitos
d3baa9c032 feat(tests): remove Time Log, move Tempo sync to Phase Timeline
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- Remove WorklogTimeline (manual time log) from test detail page
- TestPhaseTimeline now accepts testId, fetches its own worklogs,
  and shows Tempo sync status on the Red Team Execution row:
    • green badge if already synced (with worklog ID tooltip)
    • 'Sync to Tempo' button (blue) if not yet synced
- Add POST /tests/{id}/sync-tempo backend endpoint for manual sync:
  finds unsynced red_team_execution worklogs and pushes them to Tempo

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 14:09:16 +02:00
kitos
986e91a88a feat(evidence): inline preview for images and text/JSON files
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Adds a View button (eye icon) on each evidence card for previewable file
types. Opens a full-screen modal:
- Images (png/jpg/gif/webp/svg/…): rendered directly via <img> tag
- JSON: fetched authenticated, pretty-printed in green mono
- Text/log/md/csv/xml/yaml/…: fetched authenticated, shown in <pre>

Non-previewable files only show the Download button as before.
Modal closes on Escape or backdrop click.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 13:49:35 +02:00
kitos
cf5332f522 fix(evidence): use @model_validator(mode='before') so evidences appear in API responses
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
FastAPI 0.136.1 + Pydantic 2.13.4 serialises responses via TypeAdapter which
calls the compiled Rust validator directly, bypassing any Python-level
`model_validate` classmethod override. The @model_validator(mode='before')
decorator IS invoked by the Rust pipeline, so the evidence red/blue split and
technique field population now run on every serialisation path.

Also eager-load technique in get_test_detail to avoid lazy-load surprises.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 13:37:18 +02:00
kitos
2ee74bf6c9 fix(tempo): fix EU base URL, trailing space in account ID, and tempo_synced tracking
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Root causes found for Tempo worklogs never reaching Tempo:
1. Wrong API region: workspace is on api.eu.tempo.io/4 but code used api.tempo.io/4
   → Tempo returned "User is invalid" (400) for all POST /worklogs
2. Trailing space in jira_account_id stored in DB (now stripped with .strip())
3. tempo_synced field was never updated even on success (now set from Tempo response)

Fix: add tempo.base_url system_config key (admin-configurable without redeploy),
fall back to TEMPO_BASE_URL env-var, then global default. DB already updated with
https://api.eu.tempo.io/4 for this workspace.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 12:48:22 +02:00
kitos
0830b36cd6 fix(schemas): avoid lazy-load in TestOut.model_validate
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Accessing obj.evidences on a session-expired ORM object (mutation endpoints
do commit+refresh without joinload) triggers a lazy query that fails or
returns stale data. Use obj.__dict__.get('evidences') instead — SQLAlchemy
stores joinloaded relationships in __dict__; absent means not loaded.

Mutation endpoints (submit-red, submit-blue, etc.) return empty evidence
lists, which is fine: the frontend invalidates and refetches GET /tests/{id},
which uses joinedload and correctly populates red_evidences / blue_evidences.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 12:06:34 +02:00
kitos
e623a0887d fix(tempo,evidence): fix SystemExit crash + evidence not shown in frontend
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
tempo: tempoapiclient raises SystemExit (BaseException) on API errors like
'User is invalid' 400 responses; except Exception never catches it, killing
the uvicorn worker and causing a 500. Wrap create_worklog() to intercept
BaseException and re-raise as RuntimeError so callers can catch it safely.

evidence: TestOut schema was missing red_evidences / blue_evidences fields.
The ORM model has evidences loaded via joinedload but they were never
serialized into the API response. Add both fields to TestOut and override
model_validate to split Test.evidences by team, injecting the backend-proxy
download_url for each one (/api/v1/evidence/{id}/file).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 11:57:52 +02:00
kitos
0955f35015 fix(tempo,jira,tests,ui): fix 4 pending issues
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- tempo: remove unsupported `workType` kwarg from create_worklog call;
  tempoapiclient v4 does not accept it → was causing every Tempo sync to fail
- tests: set created_at=datetime.utcnow() explicitly on test creation (both
  create_test and create_test_from_template) since the DB column has no
  server default, causing 'Created —' in the UI
- jira: remove duplicate Proof of Concept section from ticket description body;
  PoC already lives in customfield_10309, no need to repeat it in description
- ui: add TestPhaseTimeline component (read-only) showing RT execution time,
  blue queue time, blue evaluation time and lead validation timestamps derived
  from test phase timestamps; placed above WorklogTimeline in test detail page

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 11:38:29 +02:00
kitos
7111debd8f fix(evidence): proxy download + fix Jira attachment signature
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Evidence download:
- Replace presigned MinIO URLs with backend proxy endpoint
  GET /api/v1/evidence/{id}/file streams the file through the backend
  so MinIO never needs to be publicly accessible from browsers
- Add download_file() helper to storage.py (internal boto3 get_object)
- download_url in EvidenceOut now points to the proxy endpoint

Jira attachment:
- Fix add_attachment call: use add_attachment_object(issue_key, BytesIO)
  instead of add_attachment(issue_key, filename=..., content=...) which
  had wrong keyword args for the installed atlassian-python-api version

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 11:26:01 +02:00
kitos
c886b6e8bb fix(jira,evidence,tempo,settings): 4-issue fix batch
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Jira — PoC custom field:
- Add customfield_10309 (Proof of Concept) to issue fields when creating
  test tickets so the attack procedure appears in the dedicated Jira field

Tempo — blue team exclusion:
- Remove blue_team_evaluation from _TEMPO_ACTIVITY_TYPES; blue team time
  is tracked internally (worklogs) for SLA but never sent to Tempo since
  blue team has no Jira access

Evidence — uploaded_at NULL fix:
- Set uploaded_at=datetime.utcnow() explicitly in upload_evidence router;
  the DB column has no server default so it was saving as NULL

Evidence — presigned URL browser access:
- Add MINIO_PUBLIC_ENDPOINT setting (config.py, docker-compose.prod.yml)
- storage.py uses a dedicated _public_client for presigned URL generation
  so browsers receive URLs with the publicly accessible hostname instead of
  the internal Docker service name (minio:9000)
- Expose MinIO port 9000 in docker-compose.prod.yml

Evidence — Jira attachment:
- After upload to MinIO, call jira.add_attachment() to attach the file to
  the linked Jira ticket (non-fatal; errors are logged and swallowed)

Settings — hide Jira/Tempo from blue team:
- ProfileSection checks user role; blue_lead and blue_tech do not see the
  Jira Integration or Tempo Integration personal settings sections

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 11:06:31 +02:00