302 Commits

Author SHA1 Message Date
kitos
72983a022b fix(evaluations): optional chaining on evalPendingData to fix TS18048
Some checks are pending
Aegis CI / lint-and-test (push) Waiting to run
2026-06-05 16:56:29 +02:00
kitos
802e8f862b feat(evaluations): bulk approve evaluation tests with 4-step confirmation modal
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Backend:
- POST /system/attck-evaluations/bulk-approve: finds all [EVAL R*] tests in
  in_review state, approves blue side, transitions to validated, recalculates
  technique statuses, audit logs each test
- GET /system/attck-evaluations/pending-count: returns count of pending eval tests

Frontend:
- BulkApproveModal: 4 mandatory checkboxes before confirm button enables
  (lab env / not org detection / metrics impact / spot-check recommendation)
- Bulk Approve button in header badge showing pending count
- Green result banner showing approved tests + techniques recalculated
- Invalidates techniques, metrics and review-queue queries on success

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 16:53:00 +02:00
kitos
93b4a700e6 fix(evaluations): results API returns list of vendors, not dict
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
The /api/results/ endpoint returns a LIST: [{name: crowdstrike, adversaries: [...]}]
Previous code called data.get() on the list → AttributeError crash on every import.

Fix: detect list vs dict response, extract the crowdstrike vendor entry first,
then get its adversaries list. Keeps legacy dict fallback just in case.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 16:42:27 +02:00
kitos
cbaa0deedd fix(evaluations): correct fallback rounds + friendlier error messages
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- Fallback names now use hyphens matching live API (carbanak-fin7, wizard-spider-sandworm)
- Add APT3 (R1) and Enterprise 2025/er7 (R7) to fallback - verified from live API
- Remove OilRig (R6) from fallback - CrowdStrike did not participate in Round 6
- Orange fallback banner only shows when NO rounds are available at all
- Soft gray note when rounds are loaded but API had transient error
- Check-new and import errors: detect 502/Cloudflare messages and show user-friendly text
  instead of raw Cloudflare HTML error messages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 16:24:06 +02:00
kitos
a4cdc06534 fix(evaluations): bypass Cloudflare 403 with browser headers + hardcoded fallback rounds
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- Add browser User-Agent and Referer headers to all evals.mitre.org requests
- fetch_rounds_with_status() returns api_reachable flag + rounds list
- Fallback to 5 known public CrowdStrike rounds (APT29/R2 through OilRig/R6)
  when live API is blocked, so UI always shows something actionable
- Router returns {rounds, api_reachable, api_error} instead of plain array
- Frontend shows orange warning banner when using fallback data
- Remove 502 HTTPException - rounds are always returned (live or fallback)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 16:10:27 +02:00
kitos
e3e79be35a feat(evaluations): ATT&CK Evaluations importer for CrowdStrike Falcon [FASE-6.1]
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- Migration b048: evaluation_imports table (adversary, round, status, tests_created)
- EvaluationImport SQLAlchemy model
- attck_evaluations_service: fetch rounds from evals.mitre.org API, import per-technique
  detection results (Technique/Tactic/Telemetry -> detected/partially/not_detected)
- All imported tests land in in_review state with lab-environment disclaimer
- Idempotency guard prevents duplicate round imports
- 4 new endpoints: list rounds, import specific, import latest, check-new
- Weekly APScheduler cron (Mon 06:00) auto-checks and imports new rounds
- SystemPage UI: rounds table, import buttons, check-new, result feedback
- Disclaimer callout reminding admins these are lab results not org coverage

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 15:57:03 +02:00
kitos
cfc48ccd2b feat(techniques): move legend to top with descriptions and review_required
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Replaces the minimal bottom legend with a full coverage legend panel
placed above the filters. Each status shows a cell mock matching the
exact colors used in the matrix, a color-coded label, and a short
description of what it means. Includes review_required with its
orange alert-triangle badge. Removes the old minimal bottom legend.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 13:23:44 +02:00
kitos
421b786953 feat(rt-import): add Image to Base64 converter utility
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
New drag-and-drop section at the bottom of the Import RT page so operators
can convert screenshots to base64 without leaving the page. Includes
thumbnail preview, copy-base64 and copy-JSON-snippet buttons with
2s feedback, per-image delete and clear-all.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 13:08:55 +02:00
kitos
14a56a6057 feat(rt-import): require base64 evidence images per technique
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Each technique in the RT import JSON now requires at least one evidence
image (PNG/JPG/GIF/WebP/BMP, max 10 MB decoded) embedded as base64.

Backend:
- RTEvidenceEntry model: filename, data (base64), caption (optional)
- RTTechniqueEntry.evidence is now required
- Pre-validation raises 422 if any technique is missing evidence
- After test creation, images are decoded and stored in MinIO as
  Evidence records (team=red) linked to the test

Frontend:
- RTEvidenceEntry type added to api/tests.ts
- parseJson() validates evidence presence and structure per technique
- Preview table shows base64 thumbnails (up to 3 + overflow count)
- Format reference updated: evidence fields moved to Required section
- Import result shows total evidence images attached

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 12:57:22 +02:00
kitos
20cdb70f57 refactor(system): rename Threat Intel Scan to Security Feed Monitor
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
The previous name implied data from a dedicated threat intelligence team.
The feature actually monitors public RSS feeds and security blogs for
ATT&CK technique mentions, so Security Feed Monitor is more accurate.
Updated description and all references across SystemPage and ReviewQueuePage.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 10:23:59 +02:00
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