Commit Graph

292 Commits

Author SHA1 Message Date
kitos
05898e2cee refactor(dashboard): replace security posture claims with programme-scoped language
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Overall Security Score renamed to Overall Programme Score. Descriptions across
Executive Dashboard and Dashboard page now clarify scores reflect Red/Blue Team
exercise maturity and coverage breadth, not the organisation real-world security
state, to avoid overstating what ATT&CK simulation tests can guarantee.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 09:33:41 +02:00
kitos
15eda30b75 fix(heatmap): hide empty tactics in threat-actor layer
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
build_threat_actor_layer was adding ALL techniques to the layer —
actor techniques with their real score and non-actor techniques with
score=0/enabled=False. This caused every tactic column to appear in
the matrix even when the actor has no techniques for that tactic.

Now only actor techniques are included. The frontend already filters
visible tactics to those with data, so empty tactic columns disappear
automatically.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-04 17:23:28 +02:00
kitos
019924f78c fix(campaigns): fix start_date modal — interceptor was losing structured detail
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
client.ts: when FastAPI detail is an object, extract .message for the error
string and preserve the full detail on enhancedError.detail so consumers
can inspect structured error payloads (e.g. 409 start_date_in_future).

CampaignDetailPage: use enhancedErr.status (not response.status) and
enhancedErr.detail (not response.data.detail) to detect 409 and show
the confirmation modal instead of the toast.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-04 16:22:17 +02:00
kitos
910c198545 fix(campaigns): correct Axios error parsing in activateMutation
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
FastAPI wraps error bodies as {detail: string | object}, not at the
top level. Was reading data.message instead of data.detail.message,
causing [object Object] in the toast for all non-409 errors.

Now correctly extracts:
- 409 with object detail -> start_date warning modal
- Other errors with string detail -> readable toast message
- Other errors with object detail -> detail.message in toast

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-04 15:57:54 +02:00
kitos
131817cc81 feat(threat-actors): Generate Campaign button on actor detail page
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Adds a Generate Campaign button (purple, visible to leads/admin) in the
threat actor header. Opens a modal with:
- Actor name shown as context
- Start date picker (required — validated: must be today or future)
- Warning message showing when tests will be queued
- Error display for API failures
- On success: redirects to the new campaign detail page

Start date is mandatory here (unlike the CampaignsPage flow where it
is optional) to enforce scheduling discipline when generating from actors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-04 15:45:55 +02:00
kitos
baac07d43a fix(ts): explicit useMutation generic types for activateMutation
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
useMutation<Campaign, unknown, boolean> to fix TS2322/TS1345/TS2345 errors
caused by inferred void variables type.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-04 15:26:12 +02:00
kitos
4c230caa32 fix(campaigns): start_date modal + hide future-campaign tests from queue
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Backend: activate endpoint returns 409 with structured warning when
start_date is in the future; accepts force=true to bypass.
test_crud_service: always excludes tests from draft campaigns with future
start_date so they do not appear in the team queue prematurely.

Frontend: catches 409 on activate and shows amber confirmation modal
with Keep scheduled / Activate now anyway options.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-04 14:05:58 +02:00
kitos
f8418bc7ea feat(campaigns): start_date for threat-actor-generated campaigns
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Backend:
- campaign_service.generate_campaign_from_threat_actor: accept optional
  start_date kwarg and set it on the Campaign model
- campaigns router: new GenerateFromActorPayload schema, /from-threat-actor
  endpoint now accepts optional body with start_date

Frontend:
- generateCampaignFromThreatActor API: accept optional options param
- Generate Campaign modal: date picker + warning message, same UX as the
  manual create form

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-04 13:37:40 +02:00
kitos
498536f3f1 fix(security): remediate CVE-2026-42043 — upgrade axios ^1.14.0
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- package.json: bump axios constraint from ^1.13.5 to ^1.14.0
- Dockerfile build stage: npm ci -> npm install so the semver range
  in package.json is honoured at build time (npm ci uses the lockfile
  exactly, bypassing the updated constraint)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-04 13:17:45 +02:00
kitos
bea5a8e781 fix(security): upgrade axios to >=1.14.0 — CVE-2026-42043 (CVSS 10)
Bumps minimum Axios version from 1.13.5 to 1.14.0 to remediate
CVE-2026-42043 identified by VMT / Wiz (asset: AegisTest).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-04 10:16:41 +02:00
kitos
c62dafbc1f feat(campaigns): campaign start date — scheduled activation, Jira start_date
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
DB: migration b047 adds start_date (DateTime nullable) + index to campaigns.

Backend:
- Campaign model: start_date field
- CampaignCreate/Update schemas: accept start_date (ISO string)
- CRUD service: persist + serialize start_date in both serializers
- Activation endpoint: blocks manual activation if start_date is in the future
  (campaign will auto-activate via scheduler)
- Scheduler: new hourly job _run_scheduled_campaign_activation — finds draft
  campaigns with start_date <= now, activates them, creates Jira tickets,
  notifies red_tech team
- Jira: campaign + test tickets now include JIRA_START_DATE_FIELD (configurable,
  default customfield_10015). Campaign uses start_date if set, else created_at.
  Tests inherit campaign start_date.
- config.py: JIRA_START_DATE_FIELD setting

Frontend:
- Campaign type: start_date field on Campaign + CampaignSummary
- CampaignCreatePayload: start_date optional field
- Create form: date picker with min=today, warning message explaining behavior
- Campaign detail header: start_date badge showing days remaining or started date

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 16:57:06 +02:00
kitos
3db9809be5 refactor(campaigns): move CampaignTimingPanel next to Progress panel
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Progress and Timing now share a 2-column grid at the top of the detail page.
Removed CampaignTimingPanel from the bottom Jira section.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 16:42:45 +02:00
kitos
7c6aaeda30 feat(compliance): add mapping confidence warnings for DORA, ISO 27001, ISO 42001
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Amber banner for DORA and ISO 27001:2022 — community-based mapping, no official CTID source.
Orange banner for ISO 42001:2023 — experimental, MITRE ATT&CK has no AI-specific techniques yet.
Each notice explains the mapping source, limitations, and what executives should consider
before using the data in formal audits or regulatory submissions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 16:37:25 +02:00
kitos
1dcff4ad20 feat(compliance): executive descriptions and mapping rationale for all 5 frameworks
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Backend: expose description in control status response, add rich business-language
descriptions to all curated controls (ISO 27001, ISO 42001, CIS v8, DORA) explaining
requirements and ATT&CK mapping rationale. ISO 42001 includes infrastructure-mapping note.

Frontend: description field in type, info panel in ControlsTable expanded rows,
framework info banner with description and official standard link in CompliancePage.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 16:28:16 +02:00
kitos
0b82d96bcc feat(compliance): add ISO/IEC 27001:2022 and ISO/IEC 42001:2023 frameworks
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
ISO 27001:2022: 37 Annex A controls across 4 themes (Organizational,
People, Physical, Technological) mapped to MITRE ATT&CK techniques.

ISO 42001:2023: 25 Annex A controls for AI Management Systems mapped to
relevant ATT&CK techniques covering AI supply chain, data pipeline
integrity, model serving security, and third-party AI risk.

Backend: import functions, _import_curated_framework() shared helper,
and POST /compliance/import/iso-27001 + iso-42001 endpoints.
Frontend: API client functions + import buttons in CompliancePage.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 15:50:54 +02:00
kitos
460faf9935 feat(disputed): symmetric UX for both leads in disputed state
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Lead who approved: Request Discussion button becomes Discussion Requested after sending.
Lead who rejected: new Change to Approved button to resolve conflict after offline discussion.
Both leads retain vote-change buttons. discussionSent state flag tracks send status.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 14:09:52 +02:00
kitos
02ff89401c fix(disputed): add admin role + contact info in discussion modal
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- request-discussion endpoint: add 'admin' to allowed roles
- Return rejector_email and rejector_role in the response
- Modal success state shows contact card with username, role, email link
  so the approving lead can immediately reach out to the rejecting lead
2026-06-03 13:02:57 +02:00
kitos
4e20bfa835 feat(disputed): Confirm My Validation button + discussion request modal
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Backend: POST /tests/{id}/request-discussion
  - Only callable by the lead whose vote is 'approved' in a disputed test
  - Sends notification to the rejecting lead: 'Lead X confirms their
    approval and wants to discuss your rejection'
  - Logs the action in audit trail

Frontend:
- 'Confirm My Validation' button (amber outline) alongside 'Change to Rejected'
- Opens a modal showing:
    * Explanation: both leads must agree to finalise
    * Other lead's rejection reason/notes
    * What happens next (stays disputed, notification sent, either can change)
- 'Send Discussion Request' → calls the new endpoint → shows success state:
    'Lead username has been notified...'
- Instruction to reach out via team channels to resolve offline

Flow summary for disputed tests:
  Approving lead sees 2 options:
    a) 'Confirm My Validation' → modal → send request → other lead notified
    b) 'Change to Rejected' → validation modal → both agree to reject → rejected
2026-06-03 12:48:08 +02:00
kitos
46ff79e695 fix(disputed): add disputed to TestState in test_entity.py
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
test_entity.py has its own TestState enum separate from domain/enums.py.
Only domain/enums.py was updated, causing AttributeError when SQLAlchemy
tried to map 'disputed' from DB to the test_entity.TestState class.

Also adds disputed to VALID_TRANSITIONS so the entity can transition
into and out of the disputed state.
2026-06-03 12:36:21 +02:00
kitos
9f86c205be fix(ts): add disputed to all Record<TestState> maps to satisfy TypeScript
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
2026-06-03 12:23:53 +02:00
kitos
61e6037e97 feat(tests): disputed state + fix timestamps on reopen
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
1. New 'disputed' state — one lead approved, the other rejected:
   - Both approved → validated (unchanged)
   - Both rejected → rejected (unchanged)
   - One approves + one rejects → disputed (new)
   - DB: ALTER TYPE teststate ADD VALUE 'disputed'
   - Notification sent to the approving lead explaining the conflict
     with the rejection notes

2. Disputed UI in TestDetailHeader:
   - Amber banner showing conflict + rejection reason from notes
   - 'Change Vote to Rejected' button for the lead who approved
   - Validation indicators shown for disputed state too

3. Fix timestamps on reopen (rejected → draft):
   - Keep red_started_at, blue_started_at etc. as historical record
   - Only clear paused_at defensively
   - Timestamps naturally update when test is re-executed

4. disputed badge (amber) added to all badge color maps

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 12:21:47 +02:00
kitos
2de95a3082 feat(tests): reopen rejected test keeps all content + rejection notes
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Backend (reopen_test):
- Preserve red/blue validation NOTES — teams see exactly what to fix
  without losing the rejection context. Previously both notes were cleared.
- Preserve all content fields: procedure_text, tool_used, red_summary,
  attack_success, blue_summary, detection_result (already the case).
- Preserve evidences (separate table, unaffected — already the case).
- Still clear: validation statuses + who/when validated (fresh re-validation
  required). Phase timing reset so the new execution starts clean.

Frontend:
- Button label: 'Reopen Test' → 'Continue Test' (more accurate intent)
- Dialog title: 'Reopen Test' → 'Continue Test'
- Dialog message: replaces alarming 'workflow will be restarted / clear all'
  with accurate description of what is preserved vs reset
- Toast: explains what to do next
2026-06-03 11:31:37 +02:00
kitos
74ca8dc53a fix(TestsPage): move lastActivityDate outside component to fix TDZ error
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
useMemo executes its factory immediately on first render. lastActivityDate
was defined with const after the useMemo call inside the component, causing
a temporal dead zone: 'Cannot access v before initialization'.

Fix: move the function to module scope (before the component), where it
is fully initialized before any hook runs.
2026-06-03 11:26:00 +02:00
kitos
ad5cd26363 fix(tests): replace updated_at (doesn't exist) with real timestamps
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
TestsPage 'Updated' column: compute lastActivityDate() from the most
recent available timestamp — blue_validated_at > red_validated_at >
blue_work_started_at > blue_started_at > red_started_at > created_at.
Also fixes the sort-by-updated_at case.

ValidatedTestsPage 'Validated' column: use blue_validated_at (when Blue
Lead approved) falling back to red_validated_at. Fixes both the display
and the default sort-by-validated.
2026-06-03 11:22:28 +02:00
kitos
fc3b413a83 fix(tests): use blue_started_at for Waiting column (updated_at doesn't exist)
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
updated_at column does not exist in the tests table — it was always
undefined, so formatElapsed() always returned '—'.

Replace with blue_started_at (set when Red Team submits to Blue Team),
which correctly shows how long a test has been waiting for Blue Team
evaluation. Also fixed the waiting_time sort to use the same field.
2026-06-03 11:15:00 +02:00
kitos
9f1c4c28c9 feat(evidence): paste screenshot directly from clipboard (Ctrl+V)
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- Global document paste listener captures image/* items from clipboard
- Auto-generates filename: screenshot-YYYY-MM-DDTHH-MM-SS.png
- Brief cyan pulse animation confirms the paste was detected
- Shows image preview before uploading (max-h 192px, object-contain)
- Drop zone hint now says 'Drag & drop, browse, or Ctrl+V to paste'
- Works with any source: OS screenshot (PrintScreen/Cmd+Shift+4),
  browser Inspect screenshots, any image copied to clipboard
2026-06-03 11:06:22 +02:00
kitos
ea8c48755f fix(tooltip): clarify Blue Team Avg Time excludes queue wait time
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
2026-06-03 11:01:50 +02:00
kitos
5684484fdf fix(metrics): prevent 0.0 falsy bug for sub-hour timing values
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Root cause: avg times were ~2-3 minutes (< 1h). round(0.033, 1) = 0.0
which is falsy in JS, so the frontend showed N/A instead of the value.

Fix (backend): _safe_stats() and team metrics now convert to minutes
when avg < 1 hour, adding a 'unit' field ('min' or 'hrs').

Fix (frontend): use != null instead of truthy check for avg_completion_hours,
MTTD, MTTR — correctly shows 0.0 and uses the unit field to show 'min' or 'hrs'.
2026-06-03 10:59:58 +02:00
kitos
06e8effaa4 fix(metrics): use direct timestamp fields instead of audit log lookups
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
MTTD: was querying AuditLog for action names that don't match actual
logged actions. Now uses red_started_at → blue_started_at directly
(both stored on the Test record). Net of red_paused_seconds.

MTTR: was searching for remediation_status=completed (no data). Redefined
as total pipeline time: red_started_at → blue_validated_at net of all
paused time. Only counts fully validated tests.

Red avg time: was using red_validated_at - created_at (created_at NULL
for many tests). Now uses blue_started_at - red_started_at net paused.

Blue avg time: was using blue_validated_at - red_validated_at (wrong
phase boundary). Now uses blue_work_started_at (or blue_started_at
fallback) → blue_validated_at net of blue_paused_seconds.
2026-06-03 10:40:05 +02:00
kitos
56d49f6de7 feat(exec-dashboard): move Red/Blue team stats above Top Threat Actors
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
2026-06-03 10:33:01 +02:00
kitos
688e843e03 feat(exec-dashboard): vertical bars for Coverage by Tactic in MITRE order
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- Convert horizontal bar chart to vertical bars (columns)
- Sort all 14 MITRE ATT&CK tactics in official order:
  Reconnaissance → Resource Development → Initial Access → Execution →
  Persistence → Privilege Escalation → Defense Evasion → Credential Access →
  Discovery → Lateral Movement → Collection → C2 → Exfiltration → Impact
- Show ALL tactics (not a subset)
- Labels rotated -45° to fit all names
- Bars have rounded top corners; horizontal gridlines only
2026-06-03 10:13:09 +02:00
kitos
e03a222ab0 fix(types): add conversion_rate fields to ValidationThroughput interface
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
2026-06-03 10:07:49 +02:00
kitos
f53500bcb5 fix(exec-dashboard): replace time-dependent throughput with Pipeline Conversion %
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
'Validation Throughput (tests/week)' was time-dependent — director wanted
an activity-based metric instead.

New metric: Pipeline Conversion Rate
  formula: validated / (validated + rejected + in_review) × 100
  unit: %  (no time reference)
  meaning: 'of all tests that have entered validation, X% succeeded'
  trend: declining if in_review backlog > validated count,
         improving if conversion ≥ 80%, stable otherwise

Backend: calculate_validation_throughput() rewritten — same API key
(tests_per_week) kept for compatibility, new conversion_rate field added.
Frontend: label → 'Pipeline Conversion', unit → '%', tooltip updated.
2026-06-03 10:06:30 +02:00
kitos
9e36b683fa feat(exec-dashboard): split threat actors into exposure vs detection strength
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Replace single list with two-column layout:
- LEFT '⚠ Highest Exposure': top 5 actors by uncovered technique count,
  red border, text explaining 'these attacks would go unnoticed today'
- RIGHT ' Strongest Detection': top 5 actors by coverage %, green border,
  text explaining 'Blue Team would likely detect an intrusion from these'

Shows both the risks (where to focus testing) and the strengths
(what's already well protected) to give executives a balanced view.
2026-06-03 10:01:22 +02:00
kitos
b33562a34e feat: add tooltip to Overall Score gauge in Executive Dashboard
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
2026-06-03 09:57:46 +02:00
kitos
757d99d22a feat(dashboards): hover tooltips on all metric cards
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
New MetricTooltip component — a small ⓘ icon showing an executive-
friendly explanation panel on hover (CSS, no JS, instant).

DashboardPage: tooltips on all 6 coverage summary cards (Total
Techniques, Validated, Partial, In Progress, Not Covered, Not
Evaluated), Coverage Evolution chart, Test Pipeline funnel,
Team Activity and Validation Rate section headers.

ExecutiveDashboardPage: tooltips on all 4 sub-scores (Coverage,
Detection, Critical, Response), Score Trend, Top Threat Actors,
4 KPIs (MTTD, MTTR, Detection Efficacy, Validation Throughput),
Coverage by Tactic, Critical Gaps table, and all 6 team metrics
(Red/Blue Tests Done, Avg Time, Rejection).

Each tooltip explains what the metric measures, what a good/bad
value looks like, and what action to take — written for non-
technical executives.
2026-06-03 09:49:58 +02:00
kitos
d896f2761d fix(tempo): enforce 1-min minimum and ceiling rounding for worklogs
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Tempo rejects durations under 60 seconds ('Duration must be at least
one minute'). Now:
  - Always send ≥ 60 s (1 minute minimum)
  - Round UP to nearest whole minute (math.ceil)
  - 2 s → 60 s, 3m20s (200s) → 240 s, 5m00s (300s) → 300 s
2026-06-03 09:08:40 +02:00
kitos
2bbc65993c fix(tests): lock editing for operators until timer starts
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
red_tech can only edit procedure/tool/summary when the test is in
red_executing state (after pressing Start Execution). In draft state they
see a read-only view and an orange hint 'Press Start Execution to begin
editing — the timer must be running first.'

blue_tech can only edit when blue_work_started_at is set (after pressing
Start Evaluation). Before that they see an indigo hint 'Press Start
Evaluation to begin editing — pick up the test first.'

red_lead, blue_lead and admin are unaffected — they retain full edit
access in all applicable states including draft.
2026-06-03 08:14:02 +02:00
kitos
46722aec19 fix(auth): silent token refresh — active sessions no longer expire mid-use
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Problem: 15-minute tokens with no refresh mechanism kicked users to login
even when actively using the app.

Fixes:
1. config.py: raise ACCESS_TOKEN_EXPIRE_MINUTES from 15 → 480 (8h).
   Reasonable for an enterprise internal tool; still configurable via env.

2. POST /auth/refresh: new endpoint that reads the current aegis_token
   cookie and issues a fresh token if the session is still valid. Returns
   the new token in the cookie + body (same shape as /auth/login).

3. frontend/api/client.ts: response interceptor now attempts a silent
   refresh on 401 before redirecting to login:
   - Calls POST /auth/refresh once per failed request
   - If refresh succeeds: retries the original request transparently
   - If refresh fails: redirects to /login as before
   - Deduplicates concurrent refresh attempts (refresh once, resolve all)
   - Never attempts refresh on /auth/refresh or /auth/login themselves

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 15:54:15 +02:00
kitos
eee0560aeb feat(admin): export/import configuration bundle for migration
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Backend: GET/POST /api/v1/admin/export-config and /import-config
  Export includes (sensitive values redacted):
  - system_configs (email/jira settings)
  - webhook_configs (secrets redacted)
  - sso_configs (private key redacted)
  - scoring_config (weights)
  - test_templates (source=custom only)
  - users (no passwords/tokens, must_change_password=True on import)
  Import is idempotent — upsert by natural keys, safe to run multiple times.

Frontend: ExportImportSection in SystemPage (admin only)
  - 'Export Configuration' → downloads aegis-config-YYYY-MM-DD.json
  - 'Import Configuration' → file picker, sends JSON, shows summary
  - Visual checklist of what is/isn't included in the export

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 15:49:51 +02:00
kitos
922fb251da fix(webhooks): auto-detect platform format for Teams/Slack/generic
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Root cause: Microsoft Teams Incoming Webhooks require MessageCard JSON
format. The service was sending generic Aegis JSON which Teams rejected
with a 400, incrementing failure_count on every dispatch.

Fix: _send_webhook() now auto-detects the target from the URL:
  - webhook.office.com / teams.microsoft.com → Teams MessageCard
    (colored card with event title + key/value facts table)
  - hooks.slack.com → Slack attachments format
  - everything else → current generic Aegis JSON

Also resets failure_count=0 in production so the webhook starts fresh.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 14:35:35 +02:00
kitos
b4a264f2bd feat(tests): require evidence upload before phase transitions
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Backend:
- submit_red_evidence: raises InvalidOperationError if no Red Team
  evidence file has been uploaded for the test
- submit_blue_evidence: raises InvalidOperationError if no Blue Team
  evidence file has been uploaded

Frontend:
- 'Submit to Blue Team' button: disabled + '⚠ Upload evidence first'
  hint when test.red_evidences is empty
- 'Submit for Review' button: same for test.blue_evidences
- Native tooltip on disabled buttons explains the requirement
- Buttons re-enable automatically after the first file is uploaded

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 14:27:15 +02:00
kitos
2b41b191bd feat(campaigns): campaign timing panel with Red/Blue aggregated metrics
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Backend: GET /campaigns/{id}/timing-summary
  Aggregates timing across all campaign tests:
  - red_execution_secs: red_started_at → blue_started_at (minus paused)
  - blue_queue_secs:    blue_started_at → blue_work_started_at
  - blue_evaluation_secs: blue_work_started_at → validated (minus paused)
  - total_secs: sum of all three phases
  Returns totals + per-test breakdown sorted by total time desc.

Frontend: new CampaignTimingPanel component replaces WorklogTimeline
  - 4 summary cards: Red Execution / Blue Queue / Blue Evaluation / Total
  - Stacked horizontal bar showing time distribution
  - Per-test breakdown with individual mini-bars and phase durations
  - Shows 'No tests started yet' when no timing data available

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 11:06:42 +02:00
kitos
a518c06653 feat(threat-actors): hover tooltip on motivation badges
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
New MotivationBadge component with CSS tooltip showing:
- espionage: goal (intelligence theft), typical behavior, examples
- financial: goal (monetary), typical behavior, examples
- destruction: goal (disrupt/destroy infra), wiper/ICS attacks, examples
- hacktivism: goal (political/ideological), defacement/leaks, examples

Used in ThreatActorsPage (card list) and ThreatActorDetailPage (header).
2026-06-02 10:50:37 +02:00
kitos
61e705ece4 fix(status-badge): show tooltip below badge (not above) to prevent clipping
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
2026-06-02 10:45:11 +02:00
kitos
2bfcc7e58c feat(status-badge): CSS hover tooltip — replaces native title attribute
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
title= attribute tooltip is browser-native, tiny, and often invisible.
New StatusBadge component uses a Tailwind group-hover absolute panel
that appears immediately on hover with:
  - Clear heading per status
  - 'Meaning' and 'Action' lines
  - Arrow pointing to the badge
  - 200ms fade-in transition

Used in TechniquesPage (list table) and TechniqueDetailPage (header).
2026-06-02 10:42:13 +02:00
kitos
7e4a44bbde feat(techniques): status hover tooltips + min 2 tests for validated
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
1. Status logic (v3): require ≥2 validated tests with 'detected' result
   to reach 'validated' status. With only 1 validated+detected test the
   technique stays 'partial' (single test is insufficient evidence).
   Backfilled existing data: T1012 and T1059.001 downgraded to 'partial'.

2. Hover tooltips on status badges in TechniquesPage and TechniqueDetailPage:
   - validated: ≥2 tests executed and detected
   - partial: some tests done but incomplete coverage
   - in_progress: tests exist but none validated yet
   - not_covered: tests run but Blue Team didn't detect
   - not_evaluated: no tests created yet
   - review_required: recent update needs acknowledgment

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 10:32:52 +02:00
kitos
ba75baeb7d fix(exec-dashboard): sort Top Threat Actors by uncovered techniques
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Previously: alphabetical order (first 5 actors from list_actors query).
Now: ranked by uncovered technique count = technique_count × (1 - coverage_pct/100).
Tiebreak: higher technique_count first (broader attack surface).

Fetches 100 actors, sorts client-side, shows top 5 with:
- Rank badge (1-5) colored red/orange/yellow/gray
- 'N uncovered / M techniques' subtitle instead of target sectors
- Coverage bar + percentage

This ensures the actors with the largest coverage gap appear first.
2026-06-02 10:19:57 +02:00
kitos
71141d9901 fix(api): add no-cache middleware to prevent Cloudflare from caching API responses
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Root cause: Cloudflare CDN was caching empty/error API responses from
/api/v1/metrics/* endpoints during the backend startup window (502 errors).
Subsequent requests were served from Cloudflare edge cache, never reaching
nginx or the backend, so the dashboard always showed empty metrics data.

NoCacheAPIMiddleware adds Cache-Control: no-store + Pragma: no-cache to
all /api/ responses so Cloudflare and browsers never cache them.
2026-06-02 10:12:13 +02:00
kitos
646ac7146e fix(dashboard): force refetch on mount + refresh button for metric widgets
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Root cause: after backend restart (502 errors on startup), metric queries
(pipeline, team, recent, validation) get cached in error state. When the
user stays on the dashboard, the component never remounts so queries don't
auto-retry.

Fixes:
1. refetchOnMount:'always' — queries ALWAYS refetch when component mounts,
   even if cached with error/stale data. Prevents stuck empty state.
2. gcTime:0 — error state is not cached; next mount starts a fresh query.
3. retry:3 — more retries before giving up (covers slow startup windows).
4. Refresh button in header — manually invalidates and refetches all 4
   metric queries with a single click. Spinner icon during refetch.
2026-06-02 09:48:59 +02:00