Commit Graph

289 Commits

Author SHA1 Message Date
kitos
d8a0b0c449 fix(jira): correct ticket hierarchy — campaigns=Epic, all tests=Task
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- Campaign issue type changed from Task to Epic (required to nest under
  Initiative OFS-20795 in classic Jira)
- Added customfield_10011 (Epic Name) — required when creating Epics
- Removed JIRA_ISSUE_TYPE_SUBTASK; all tests are now Task regardless of
  whether they are inside a campaign or standalone
- Standalone tests use the configured standalone parent (OFS-20798, an
  Epic) so Task→Task parent is never attempted
- Campaign tests use the campaign Epic key passed via parent_ticket_override

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 16:29:50 +02:00
kitos
27184627f8 fix(jira): standalone tests as Sub-task under OFS-20798
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
OFS-20798 is a Task (child of OFS-20795 Epic), so tests nested
under it must be Sub-tasks, not Tasks — Task cannot parent Task.

Logic:
- parent_ticket_override (campaign) → Sub-task (unchanged)
- standalone_parent configured and differs from general parent → Sub-task
- only general parent (Epic) → Task

This fixes 'Please select valid parent issue' for standalone tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 16:19:01 +02:00
kitos
323964ed9d fix(settings): rename Campaign Parent Ticket label to Parent Ticket
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
The field is the general parent (e.g. OFS-20795) under which campaigns
are created directly. 'Campaign Parent Ticket' was misleading.
Standalone Tests Parent Ticket remains separate (e.g. OFS-20798).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 13:14:48 +02:00
kitos
eeee17d260 feat(jira+tests): 5 improvements from review
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
1. Jira status → In Progress on Start Execution
   - push_test_event calls set_issue_status("In Progress") when
     new_state == "red_executing" (non-fatal, separate try/except)

2. Jira assignee set on Start Execution
   - assign_issue() called with actor.jira_account_id when operator
     clicks Start (non-fatal)

3. Standalone tests parent ticket (OFS-20798)
   - New jira.parent_ticket_standalone config key
   - get_jira_parent_ticket_standalone() falls back to parent_ticket
   - auto_create_test_issue uses standalone parent for non-campaign tests
   - Exposed in /system/jira-config GET+PATCH and SettingsPage UI

4. Tests table: Created + Updated columns
   - Add Created column (created_at), fix Updated to show updated_at
   - Both use UTC-aware date parsing (append Z if no tz suffix)
   - updated_at added to Test TypeScript interface

5. Sortable columns in tests table
   - All 7 columns sortable: Name, Technique, State, Current Team,
     Platform, Created, Updated
   - Click to sort asc, click again to reverse; ChevronUp/Down indicator
   - Default sort: Created desc (newest first)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 13:07:46 +02:00
kitos
43c8b241dc fix(timer): treat backend timestamps as UTC to fix 2h offset
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Backend returns naive UTC datetimes without 'Z' suffix. JavaScript
new Date("2026-05-27T09:29:18") parses as local time (UTC+2 in Spain),
making the timer start at 02:00:06 instead of 00:00:00.

Fix: append 'Z' to any timestamp string that lacks timezone info before
passing it to new Date(), so the browser always interprets it as UTC.
Applied to both startedAt and pausedAt in LiveTimer.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 11:58:29 +02:00
kitos
398e279116 feat(tempo): blue team Tempo time from pick-up, not queue entry
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Previously blue_started_at was set when the RED team submitted evidence
(= queue open time), so Tempo was getting total queue wait time instead
of actual work time.

Changes:
- DB: add blue_work_started_at column (migration b045), set when a blue
  tech explicitly picks up the test (mirrors red_started_at for red team)
- Workflow: new start_blue_work() function + POST /tests/{id}/start-blue-work
  endpoint (blue_tech / blue_lead roles). Cannot be called twice.
- submit_blue_evidence: uses blue_work_started_at (when available) as the
  phase start for the Tempo worklog, falls back to blue_started_at
- reopen_test: clears blue_work_started_at alongside other timing fields
- Tempo: both red_team_execution and blue_team_evaluation now synced;
  correct work_date and description per activity type
- Frontend: "Start Evaluation" button shown in blue_evaluating state when
  blue_work_started_at is null; live timer shows from pick-up time

What each timestamp tracks:
  blue_started_at      = queue entry (SLA / internal tracking)
  blue_work_started_at = pick-up by blue tech (Tempo start)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 11:50:15 +02:00
kitos
0e6cec4d07 fix(tempo): only log red team execution time, use pre-computed duration
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Two bugs fixed:

1. Blue team evaluation was also sent to Tempo. Only operator (red team)
   execution time should be logged — blue team time is tracked internally
   in Aegis but does NOT represent billable operator work. Added a
   whitelist (_TEMPO_ACTIVITY_TYPES = {"red_team_execution"}).

2. _calculate_duration() re-computed duration from red_started_at to
   datetime.utcnow() at call time, without subtracting paused seconds.
   This caused inflated times (e.g. 45 min instead of 5 min) when there
   was any delay between the workflow transition and the Tempo call.
   Now the duration_seconds already computed by _create_phase_worklog
   (gross elapsed - paused) is passed directly to auto_log_test_worklog
   and used as-is, so Aegis and Tempo always agree on the duration.

Also: use red_started_at as the worklog date (not submission timestamp)
so the Tempo entry reflects when the work actually happened.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 11:38:44 +02:00
kitos
44ef4129a5 fix(tempo): use search_worklogs(authorIds) in test endpoint
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
get_worklogs_by_account_id does not exist in tempoapiclient v4.
The correct method is search_worklogs(dateFrom, dateTo, authorIds=[...]).
Also improve error messages: 401 points to where to get the token,
404 tells the user the Account ID may be wrong.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 11:25:15 +02:00
kitos
bd0586d296 fix(jira): campaign=Task, campaign tests=Sub-task, standalone tests=Task
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Root cause: Jira rejects Task-under-Task nesting ("Please select valid
parent issue"). Campaign tickets and test tickets were both created as
Task, so nesting test under campaign failed for all 62 APT32 tests.

Fix:
- JIRA_ISSUE_TYPE_CAMPAIGN: "Epic" -> "Task" (was unused, now used)
- JIRA_ISSUE_TYPE_SUBTASK: "Sub-task" (new config key)
- auto_create_campaign_issue: uses JIRA_ISSUE_TYPE_CAMPAIGN (Task)
- auto_create_test_issue: uses Sub-task when parent_ticket_override is
  set (campaign context), Task otherwise (standalone)

Hierarchy: OFS-9107 -> Campaign (Task) -> Test (Sub-task)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 11:10:03 +02:00
kitos
84a6590e17 fix(jira): create test tickets under campaign on activation
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
When a campaign is activated (Start), iterate all its tests and create
Jira tickets nested under the campaign ticket for any test that doesn't
already have one. Mirrors the pattern used in generate_campaign_from_actor.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 10:53:39 +02:00
kitos
69d92f500a feat(tempo): per-user Tempo API token — same pattern as Jira token
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Each user can now store their own personal Tempo API token in their
profile settings. Time is logged using each user's own credentials.

Backend:
- Migration b044: adds tempo_api_token column to users table
- User model: adds tempo_api_token column
- UserPreferencesUpdate: adds tempo_api_token field (write-only)
- UserOut: adds tempo_api_token (excluded) + tempo_token_set bool;
  @model_validator derives both jira_token_set and tempo_token_set
- users router: handles tempo_api_token same as jira_api_token
  (empty string clears it, never returned in responses)
- tempo_service: refactored to per-user token; has_tempo_configured(),
  get_user_tempo_client(user) use user.tempo_api_token; global
  TEMPO_ENABLED still acts as kill-switch
- system router: /system/tempo-test now uses current user's personal
  token (any role); removed global TEMPO_API_TOKEN dependency

Frontend:
- settings.ts: UserPreferencesUpdate.tempo_api_token, UserMeOut.tempo_token_set
- SettingsPage ProfileSection: Tempo Integration section with password
  field, show/hide toggle, configured badge, and Test Tempo button —
  mirrors the Jira token UX exactly
- JiraConfigSection: removed stale global Tempo test block

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 10:46:38 +02:00
kitos
2337abe55e fix(jira): correct browse URL, rename Procedure to Proof of Concept; feat(tempo): debug endpoint + UI
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Jira URL fix:
- JiraLinkPanel now fetches the configured Jira base URL via getJiraConfig()
  instead of hardcoding https://jira.atlassian.com; falls back to the old
  value if config is not yet loaded

Description fix:
- _build_test_description: renamed 'h3. Procedure' -> 'h3. Proof of Concept'
  so the procedure/tool block maps to the correct Jira field label

Tempo debug:
- New POST /system/tempo-test endpoint: checks TEMPO_ENABLED, token,
  user jira_account_id, and makes a real API call; always returns HTTP 200
  with status field (Cloudflare-safe)
- docker-compose.prod.yml: added TEMPO_ENABLED, TEMPO_API_TOKEN,
  TEMPO_DEFAULT_WORK_TYPE env vars (default off, ready to enable)
- SettingsPage: added 'Test Tempo Connection' button in Jira admin tab
  with clear feedback showing what's missing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 10:33:57 +02:00
kitos
4a64ac1c8b fix(jira): remove priority field from issue creation — OFS project has non-standard priorities
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
The OFS Jira project does not have the default Jira priority scheme
(Highest/High/Medium/Low/Lowest), causing a 'priority selected is invalid'
error on every ticket creation. Removing the priority field lets Jira use
the project default.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 10:18:16 +02:00
kitos
f17f0a8c10 feat(jira): implement full ticket hierarchy for campaigns and tests
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Jira tickets now follow the correct hierarchy:
  OFS-9107 (system parent)
  ├── Standalone test ticket  (unchanged — was already working)
  └── Campaign ticket         (NEW — created on campaign creation)
      ├── Test 1 ticket       (NEW — created per test)
      └── Test 2 ticket       (NEW — created per test)

Changes:
- jira_service: add auto_create_campaign_issue() — creates campaign
  ticket as child of OFS-9107; stores JiraLink(entity_type=campaign)
- jira_service: add get_campaign_jira_key() / get_test_jira_key()
  helpers to look up existing Jira links by entity
- jira_service: auto_create_test_issue() gains parent_ticket_override
  param — when set, uses it as parent instead of OFS-9107
- campaigns router/create_campaign: triggers auto_create_campaign_issue
  after commit
- campaigns router/from-threat-actor: triggers campaign ticket then
  iterates campaign_tests and creates each test ticket under it
- campaigns router/add_test_to_campaign: if campaign has a Jira ticket
  and the test has none yet, creates test ticket under campaign ticket

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 10:13:09 +02:00
kitos
5f6a098e6b fix(jira): fallback connected_as to auth email, improve 401 error detail
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- jira-test: when myself() returns empty displayName/emailAddress/name,
  fall back to the configured Atlassian auth email so 'Connected as:' is
  never empty
- jira-test: 401 error message now includes which email was used, making
  misconfigured Jira email easier to diagnose
- jira-test: missing jira_url now returns HTTP 200 {status: error} instead
  of HTTP 400, consistent with Cloudflare-safe pattern

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 18:04:51 +02:00
kitos
a04d5308ab fix(jira): always return HTTP 200 from jira-test + strip trailing slash
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- jira-test now returns {status: "ok"|"error", message: ...} with
  HTTP 200 so Cloudflare never intercepts the response
- jira_service strips trailing slash from URL before creating Jira
  client (avoids double-slash in REST paths)
- Frontend reads data.status field instead of HTTP status code

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 17:42:12 +02:00
kitos
48a936d426 fix(jira): use model_validator(after) for jira_token_set + timeout on test
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
FastAPI uses __pydantic_validator__.validate_python() which bypasses
model_validate() overrides. Switch to @model_validator(mode='after')
which the Pydantic Rust core always calls, so jira_token_set is now
correctly derived from the excluded jira_api_token field.

Also add a 10s timeout to the jira-test endpoint and better error
messages (the Atlassian library's "Expecting value" JSON error was
ambiguous).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 17:36:35 +02:00
kitos
513a7b488b fix(settings): update cache immediately on save instead of invalidating
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Using setQueryData with the PATCH response means jira_token_set is
reflected in the UI instantly — no extra GET round-trip that could
leave the badge stale.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 17:20:40 +02:00
kitos
fd4a625760 fix(settings): use useEffect for jira field init, fix token save UX
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Replace render-body setState with useEffect so field initialisation
is idiomatic React and never races with user input. Also clarifies
placeholder text: empty token field = keep current, not clear it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 17:04:22 +02:00
kitos
217c4c88b2 feat(jira): add editable jira_email field per user
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Users can now set a separate Atlassian email for Jira authentication
in Settings → Profile → Jira Integration. Falls back to the Aegis
account email when not set, so existing setups are unaffected.

- Migration b043: adds jira_email column to users table
- User model/schema: expose jira_email read/write
- jira_service: _effective_jira_email() uses jira_email ?? email
- Frontend: replaces read-only email display with editable input

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 16:40:46 +02:00
kitos
f316a249cc feat(settings): Jira config UI — admin config tab + per-user token in Profile
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- backend: add parent_ticket field to JiraConfigOut/JiraConfigUpdate/_JIRA_KEYS
- backend: add get_jira_parent_ticket() helper in jira_service; use it in auto_create_test_issue() to set issue parent
- frontend/api: add jira_token_set to UserMeOut, jira_api_token to UserPreferencesUpdate, and full JiraConfigOut/Update types with getJiraConfig/updateJiraConfig/testJiraConnection functions
- frontend: expand ProfileSection with Jira API token password field (show/hide), token status badge, and account-id field
- frontend: add JiraConfigSection component (admin): enabled toggle, URL, project key, parent ticket, save + test connection
- frontend: add Jira tab (admin-only) with Link2 icon in SettingsPage sidebar
2026-05-26 16:23:24 +02:00
kitos
2675a4b7c2 fix(jira): correct down_revision id in b042 migration
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
2026-05-26 15:59:23 +02:00
kitos
c780ad1e78 feat(jira): per-user auth, lifecycle hooks, admin config endpoints
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- Add jira_api_token field to User model + migration b042
- Per-user Jira client: user's corporate email + personal Atlassian token
- Admin-configurable Jira URL/project via system_configs (GET/PATCH /system/jira-config + POST /system/jira-test)
- Auto-create Jira ticket when a test is created (non-fatal)
- Push lifecycle comments on every state transition: draft→red_executing→blue_evaluating→in_review→validated/rejected→draft
- Rich ticket descriptions with technique, MITRE ID, priority from severity, labels
- UserOut.jira_token_set (bool) instead of exposing raw token
- PATCH /users/me/preferences now accepts jira_api_token

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 15:56:28 +02:00
kitos
8bed3abc08 docs(wiki): add wiki creation script for Gitea
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Creates 14 comprehensive wiki pages covering architecture, roles,
test lifecycle, API reference, security, deployment, and QA guide.
Run from a machine with access to internal Gitea (192.168.1.107:3000).
2026-05-22 14:30:21 +02:00
kitos
c45eed2801 test(qa): fix all test failures - 77/77 passing
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- Accept 409 for playbook creation (unique per technique+type is correct behavior)
- Space logins 13s apart to avoid 5/min rate limit on login endpoint
- Reuse admin session from initial login to avoid duplicate login call
2026-05-22 11:05:24 +02:00
kitos
cba9bfbab9 security(webhooks): restrict all webhook endpoints to admin-only
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
fix(qa): pass technique_id and test_id context between test suites
fix(qa): playbook creation requires technique_id field
fix(qa): lesson creation requires what_happened and root_cause fields
fix(qa): campaign complete test now activates with test before completing
fix(qa): rate limit test notes loopback exemption instead of failing
2026-05-22 10:56:15 +02:00
kitos
43ef4ea6a0 test(qa): add automated QA runner for all roles and access control
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
2026-05-22 10:30:54 +02:00
kitos
6f4901b611 security: fix 6 vulnerabilities identified in SDLC audit
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- fix(auth): enforce API key scopes in require_role/require_any_role;
  attach _api_key_scopes to user on API key auth; add require_scope()
  dependency — scopes were stored but never enforced (CWE-285)

- fix(sso): read SECURE_COOKIES env var for SSO cookie instead of
  hardcoded secure=False — SAML sessions now respect HTTPS config (CWE-614)

- fix(webhooks): SSRF prevention — validate webhook URLs against private
  and reserved CIDRs at creation/update time (CWE-918)

- fix(knowledge): restrict playbook/lesson create, update and restore
  to admin/red_lead/blue_lead roles — was open to any authenticated user (CWE-284)

- fix(alerts): restrict alert acknowledge/resolve/dismiss to admin/lead
  roles — any user could silence security alerts (CWE-284)

- security: delete get_admin_creds.py, check_auth.py, deploy.py scripts
  containing hardcoded root SSH credentials and production DB access;
  add scripts/.gitignore to prevent reintroduction (CWE-798)
2026-05-22 09:46:29 +02:00
kitos
f36c633d16 fix(scripts): fix verify_gaps.py Gap 1 check — call start_scheduler() before checking registered jobs
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
2026-05-21 17:28:34 +02:00
kitos
fc16675cf2 fix(alerts): import User model in operational_alert_service to fix NameError in _dispatch_inapp_notifications
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
2026-05-21 17:11:35 +02:00
kitos
d05aa94a01 test: gap verification script for Phase 13 gaps
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
2026-05-21 16:08:45 +02:00
kitos
97349a1d13 feat(alerts): close Phase 13 gaps — hourly job + webhook + in-app notifications
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- Add dispatch_webhook_targeted() to webhook_service for rule-specific delivery
- evaluate_all_rules() now dispatches in-app notifications (admins/leads) and
  webhooks after each alert fires (targeted + global alert.fired broadcast)
- APScheduler: _run_alert_evaluation() job registered hourly alongside existing jobs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 15:57:41 +02:00
kitos
cfbf6a6ede fix(dashboard): make KpiBlock.snapshot_id Optional to handle missing today snapshot
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
2026-05-21 15:27:26 +02:00
kitos
d4b147da7c feat(alerts): Phase 13 — Operational Alert Engine
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
AlertRule + AlertInstance models (b041alerts migration), 8 pre-seeded system
rules (high_risk x2, stale_technique, coverage_regression, low_coverage,
expiry_wave, new_technique, orphan_spike), evaluation engine with per-rule
cooldown, full alert lifecycle (acknowledge/resolve/dismiss), custom rule CRUD,
and summary endpoint. Rules seeded at app startup.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 15:25:55 +02:00
kitos
d81fc04b8f feat(enterprise): Phase 14 — API Key Management + SSO/SAML 2.0
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- ApiKey model (SHA-256 hash, prefix, scopes, expiry) + Alembic migration (b040ent)
- SsoConfig model for SAML 2.0 IdP settings (attribute mapping, auto-provision)
- API key auth integrated into get_current_user (aegis_ prefix detection)
- Routers: /api/v1/api-keys (full CRUD + revoke) and /api/v1/sso (metadata, login, callback, config)
- python3-saml added to requirements; Dockerfile adds libxmlsec1-dev for SAML XML signing
- QA script: 52 assertions covering key lifecycle, API key auth, SSO config

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 16:43:57 +02:00
kitos
ab591d30c4 feat(dashboard): Phase 13 — Executive Dashboard
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
PostureSnapshot model, Alembic migration (b039exec), schemas, service
aggregating all phases (coverage/risk/operations/knowledge/MTTD), and
router at /api/v1/dashboard with executive view, KPIs, coverage-by-tactic,
posture-history, posture-snapshot, and activity-feed endpoints.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 16:20:21 +02:00
kitos
41a0c536bb fix(risk): fix remaining t.technique_id → t.mitre_id in get_recommendations
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
2026-05-20 16:11:48 +02:00
kitos
7fae4783a2 fix(risk): Technique uses status_global and mitre_id (not status/technique_id)
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
2026-05-20 15:59:26 +02:00
kitos
084ea4c0b2 fix(risk): correct TechniqueConfidenceScore fields, TechniqueStatus values, Test.result usage
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
2026-05-20 15:58:03 +02:00
kitos
362a17aa1b feat(risk): Phase 12 — Risk Intelligence [FASE-12]
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- TechniqueRiskProfile model: per-technique risk scoring (0-100)
- 4-factor weighted scoring: detection_gap(35%) + threat_actors(30%) + osint(20%) + test_failures(15%)
- Risk levels: critical(≥75) / high(≥50) / medium(≥25) / low(≥10) / info
- Detailed scoring_breakdown (JSONB) + actionable recommendations per technique
- Router /api/v1/risk: compute-all, compute-one, list, matrix, summary, recommendations, top
- Alembic migration b038risk (raw SQL, idempotent)
- QA script: 60+ tests across all endpoints

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 15:31:38 +02:00
kitos
0febbc67f1 fix(qa11): use relative version checks for idempotent runs
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
2026-05-20 15:26:38 +02:00
kitos
852adb6e4d fix(qa11): make QA idempotent with cleanup step + robust error handling
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
2026-05-20 15:25:46 +02:00
kitos
4fba4152d9 fix(knowledge): use EntityNotFoundError/DuplicateEntityError instead of DomainError(status_code=)
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
2026-05-20 15:21:36 +02:00
kitos
9546ef8bc8 fix(qa11): use correct production credentials
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
2026-05-20 15:14:58 +02:00
kitos
e550ebb30f fix(qa11): use production admin credentials
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
2026-05-20 14:31:46 +02:00
kitos
5e18db48d3 fix(qa11): fix get_token to use form data + fix check() bug
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
2026-05-20 14:27:41 +02:00
kitos
4f5370db89 feat(knowledge): Phase 11 — Knowledge Management (Playbooks + Lessons Learned) [FASE-11]
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- Playbooks: versioned Markdown runbooks per technique × type (attack/detect/investigate/respond/hunt)
- PlaybookVersion: immutable snapshots on every update; restore to any previous version
- LessonLearned: post-mortem records linked to tests/campaigns/attack-paths or manual
- Alembic migration b037know (raw SQL, idempotent, no PostgreSQL enums)
- Router /api/v1/knowledge: 14 endpoints for playbooks + lessons + stats
- Pydantic validators for playbook_type, severity, entity_type (422 on invalid)
- Knowledge stats endpoint: totals + breakdown by severity and playbook type
- Soft-delete on both resources; include_inactive filter for admin recovery
- QA script: 70+ tests across CRUD, versioning, filtering, auth, soft-delete, regression

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 13:39:05 +02:00
kitos
080ce56de7 feat(attack-paths): Phase 10 — Attack Paths & Advanced Purple Team [FASE-10]
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Models (5 tables):
  - AttackPath: named reusable attack scenario with template flag
  - AttackPathStep: ordered kill-chain step (technique + test link)
  - AttackPathExecution: a run with Red/Blue leads, timing, stored metrics
  - AttackPathStepResult: per-step detected/not_detected/skipped result
  - TimelineEntry: timestamped Red/Blue/system actions for MTTD/MTTR

Migration b036atk: raw SQL to avoid SQLAlchemy DDL hook issues

Service (attack_path_service.py):
  - Full CRUD for paths + steps (add, update, delete, reorder)
  - Execution lifecycle: create → start → execute steps → complete/abort
  - Pre-creates pending step results on execution creation
  - Auto-adds system timeline entries on key state transitions
  - complete_execution() computes: detection_rate, mttd_seconds,
    furthest_undetected_step, detected/not_detected/skipped counts
  - get_kill_chain_metrics(): per-step breakdown + phase summary

Router /api/v1/attack-paths (20 endpoints):
  POST/GET/PATCH/DELETE attack paths
  GET/POST/PATCH/DELETE steps + reorder
  POST/GET executions per path
  GET/POST/start/complete/abort executions
  POST/GET step results
  POST/GET timeline entries
  GET kill-chain metrics

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 13:11:01 +02:00
kitos
4ece2293ec fix(ownership): validate reason+priority in QueueItemCreate to return 422 not 500
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
POST /ownership/queue with an invalid reason or priority was silently
passing Pydantic and crashing at the DB layer (PostgreSQL enum type
mismatch → 500). Added @field_validator for both fields, matching the
existing validators in QueueItemPatch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 17:57:34 +02:00
kitos
f97b9e96b7 fix(migration): rewrite b035 with raw SQL to avoid SQLAlchemy DDL hook
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
SQLAlchemy fires before_create for ALL known enum types when any table
is created via op.create_table, causing DuplicateObject even with
create_type=False. Rewrite both CREATE TABLE statements as raw SQL via
conn.execute(sa.text(...)) and use CREATE TABLE IF NOT EXISTS / CREATE
INDEX IF NOT EXISTS for full idempotency.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 16:54:32 +02:00