From 8bed3abc08460f84cfb161da2f0c0e09b9c42d4b Mon Sep 17 00:00:00 2001 From: kitos Date: Fri, 22 May 2026 14:30:21 +0200 Subject: [PATCH] docs(wiki): add wiki creation script for Gitea 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). --- scripts/create_wiki.py | 3338 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3338 insertions(+) create mode 100644 scripts/create_wiki.py diff --git a/scripts/create_wiki.py b/scripts/create_wiki.py new file mode 100644 index 0000000..892217b --- /dev/null +++ b/scripts/create_wiki.py @@ -0,0 +1,3338 @@ +#!/usr/bin/env python3 +"""Create all Aegis wiki pages via Gitea API.""" + +import base64 +import sys +import time + +import requests +from requests.auth import HTTPBasicAuth + +# Use internal Gitea URL (bypasses reverse proxy that blocks wiki API via HTTPS) +# External HTTPS proxy blocks POST/PATCH to wiki endpoints (405 Method Not Allowed) +# The correct Gitea wiki API endpoints are: +# POST /api/v1/repos/{owner}/{repo}/wiki/new → create +# PATCH /api/v1/repos/{owner}/{repo}/wiki/page/{pageName} → update +# GET /api/v1/repos/{owner}/{repo}/wiki/pages → list +GITEA_URL = "http://192.168.1.107:3000" +OWNER = "kitos" +REPO = "Aegis" +USERNAME = "kitos" +PASSWORD = "T'2JY%HLX\"Bp^6e" + + +def create_or_update_page(title: str, content: str) -> bool: + """Create a wiki page via Gitea API. Returns True on success.""" + auth = HTTPBasicAuth(USERNAME, PASSWORD) + encoded = base64.b64encode(content.encode("utf-8")).decode("ascii") + + payload = { + "content": encoded, + "message": f"Add wiki page: {title}", + "title": title, + } + + # Gitea wiki create endpoint is /wiki/new (not /wiki/pages) + create_url = f"{GITEA_URL}/api/v1/repos/{OWNER}/{REPO}/wiki/new" + r = requests.post(create_url, json=payload, auth=auth, timeout=30) + + if r.status_code in (201, 200): + print(f" [OK] Created: {title}") + return True + elif r.status_code in (409, 422) or "already exist" in r.text.lower(): + # Page already exists — update it + slug = title.replace(" ", "-") + update_url = f"{GITEA_URL}/api/v1/repos/{OWNER}/{REPO}/wiki/page/{slug}" + r2 = requests.patch(update_url, json=payload, auth=auth, timeout=30) + if r2.status_code in (200, 201): + print(f" [OK] Updated: {title}") + return True + print(f" [FAIL-UPDATE] {title}: {r2.status_code} {r2.text[:200]}") + return False + print(f" [FAIL] Update {title}: {r2.status_code} {r2.text[:200]}") + return False + + print(f" [FAIL] {title}: {r.status_code} {r.text[:200]}") + return False + + +# ───────────────────────────────────────────── +# PAGE CONTENT DEFINITIONS +# ───────────────────────────────────────────── + +PAGE_HOME = """\ +# Aegis — Knowledge Base Home + +**Aegis** is a MITRE ATT&CK coverage management platform for Purple Team operations. +It provides a structured workflow for Red and Blue teams to plan, execute, evaluate, +and track security tests mapped to MITRE ATT&CK techniques, measure detection coverage, +manage knowledge, and generate executive reporting. + +--- + +## Table of Contents + +| Wiki Page | Description | +|-----------|-------------| +| [Home](Home) | This page — navigation hub and quick start | +| [Architecture](Architecture) | Tech stack, Docker services, backend structure, data flow | +| [Roles-and-Permissions](Roles-and-Permissions) | RBAC roles, permission matrix, admin bypass | +| [Test-Lifecycle](Test-Lifecycle) | State machine: draft → validated, all transitions | +| [MITRE-ATT-CK-Coverage](MITRE-ATT-CK-Coverage) | Coverage model, scoring, sync, heatmap | +| [API-Reference](API-Reference) | All 28 routers with endpoints, methods, required roles | +| [Authentication-and-Security](Authentication-and-Security) | JWT, cookies, API keys, SAML SSO, rate limiting | +| [Campaigns](Campaigns) | Campaign types, lifecycle, threat-actor import | +| [Knowledge-Management](Knowledge-Management) | Playbooks, lessons learned, versioning | +| [Operational-Alerts](Operational-Alerts) | Alert rules, instances, evaluation, webhooks | +| [Executive-Dashboard-and-Reports](Executive-Dashboard-and-Reports) | KPIs, snapshots, PDF/DOCX/HTML report generation | +| [Detection-Lifecycle](Detection-Lifecycle) | Detection assets, validations, infrastructure changes | +| [Deployment-Guide](Deployment-Guide) | Docker deployment, environment variables, migrations | +| [QA-Testing-Guide](QA-Testing-Guide) | Automated QA runner, manual checklists per role | + +--- + +## Quick Start + +### 1. Log In + +```http +POST /api/v1/auth/login +Content-Type: application/x-www-form-urlencoded + +username=admin&password= +``` + +The server sets an `aegis_token` HttpOnly cookie. All subsequent requests are +authenticated automatically by the browser (or pass the token as +`Authorization: Bearer `). + +On **first login**, if the admin account was freshly seeded, you will receive a +`403 PASSWORD_CHANGE_REQUIRED` on every endpoint except `/api/v1/auth/me` and +`/api/v1/auth/change-password`. Change your password immediately: + +```http +POST /api/v1/auth/change-password +Content-Type: application/json + +{"current_password": "", "new_password": ""} +``` + +### 2. Create Your First Test + +1. **Create a test** (red_lead / blue_lead / admin): + ```http + POST /api/v1/tests + {"title": "T1059.001 PowerShell execution", "technique_id": "T1059.001"} + ``` +2. **Start execution** (red_tech / red_lead / admin): + ```http + POST /api/v1/tests/{id}/start-execution + ``` +3. **Fill in red-side data** (red_tech / red_lead / admin): + ```http + PATCH /api/v1/tests/{id}/red + {"tool_used": "Cobalt Strike", "command_executed": "powershell -nop -enc ..."} + ``` +4. **Submit red** → moves to `blue_evaluating`. +5. **Fill in blue detection data** (blue_tech / blue_lead / admin): + ```http + PATCH /api/v1/tests/{id}/blue + {"detection_result": "detected", "detection_notes": "SIEM alert fired"} + ``` +6. **Submit blue** → moves to `in_review`. +7. **Validate red** (red_lead / admin) and **validate blue** (blue_lead / admin). +8. Test reaches `validated`. Technique coverage is updated automatically. + +### 3. Explore Coverage + +Navigate to the heatmap to see your MITRE ATT&CK coverage: +```http +GET /api/v1/heatmap +``` + +Or check overall scores: +```http +GET /api/v1/scores/organization +``` + +--- + +## Key Concepts Glossary + +| Term | Definition | +|------|------------| +| **Technique** | A MITRE ATT&CK technique (e.g. T1059.001). The atomic unit of coverage. | +| **Test** | An execution of an attack technique against a target environment. Has a full state machine lifecycle. | +| **Campaign** | A collection of tests grouped under a common objective (e.g. APT29 simulation). | +| **Playbook** | Step-by-step procedure for attacking, defending, or detecting a technique. Versioned. | +| **Lesson** | A "lessons learned" record linked to any entity. Captures what happened, root cause, and improvement actions. | +| **Coverage** | The status of a technique: `not_covered`, `partial`, or `validated`. Determined by test outcomes. | +| **Snapshot** | A point-in-time capture of overall coverage metrics. Used for trend analysis. | +| **Detection Asset** | A defensive capability (SIEM rule, EDR policy, sensor) mapped to one or more techniques. | +| **Alert Rule** | A condition that triggers an operational alert when met (e.g. coverage drops below threshold). | +| **Score** | A weighted numeric representation of coverage quality (0–100) for a technique or the entire organization. | +| **Threat Actor** | A known adversary profile linked to techniques. Campaigns can be auto-generated from threat actor profiles. | +| **API Key** | A long-lived credential for machine-to-machine access. Scoped to `read`, `write`, or `admin`. | + +--- + +## Where to Go Next + +- First-time deployers → [Deployment-Guide](Deployment-Guide) +- Understand who can do what → [Roles-and-Permissions](Roles-and-Permissions) +- Deep-dive into test states → [Test-Lifecycle](Test-Lifecycle) +- All API endpoints → [API-Reference](API-Reference) +- How coverage scoring works → [MITRE-ATT-CK-Coverage](MITRE-ATT-CK-Coverage) +- QA and testing → [QA-Testing-Guide](QA-Testing-Guide) +""" + +PAGE_ARCHITECTURE = """\ +# Architecture + +This page describes the technical architecture of Aegis: its components, data flow, +deployment model, and backend code structure. + +--- + +## Tech Stack + +| Layer | Technology | Version | +|-------|------------|---------| +| Backend framework | FastAPI | Latest (Python 3.11) | +| Database | PostgreSQL | 16 | +| Object storage | MinIO | Latest | +| Cache / token blacklist | Redis | 7 | +| Frontend framework | React | 19 | +| Language (frontend) | TypeScript | 5 | +| Build tool | Vite | 7 | +| CSS framework | Tailwind CSS | v4 | +| Data fetching | TanStack Query | v5 | +| ORM | SQLAlchemy | 2.x | +| Migrations | Alembic | Latest | +| Task scheduler | APScheduler | 3.x | +| Linting | Ruff | Latest | +| Testing | Pytest | 8.x | + +--- + +## Docker Services + +All services are defined in `docker-compose.yml` (development) and +`docker-compose.prod.yml` (production). + +| Service name | Image | Internal port | Dev exposed port | Role | +|---|---|---|---|---| +| `aegis-backend` | Custom (Dockerfile) | 8000 | 8000 | FastAPI application + APScheduler | +| `aegis-frontend` | Custom (Dockerfile.prod / vite dev) | 5173 / 80 | 5173 | React SPA | +| `aegis-postgres` | `postgres:16` | 5432 | 5433 | Primary relational database | +| `aegis-redis` | `redis:7` | 6379 | 6379 | Token blacklist, rate limiting, cache | +| `aegis-minio` | `minio/minio` | 9000 (API), 9001 (Console) | 9000 / 9001 | Evidence file storage (S3-compatible) | + +### Health checks + +- Backend: `GET /health` returns `{"status": "ok"}` +- Postgres: `pg_isready` via Docker healthcheck +- Redis: `PING` via Docker healthcheck + +--- + +## Backend Folder Structure + +``` +backend/ +├── app/ +│ ├── domain/ +│ │ ├── entities/ # Core domain entities (Test, Technique, User, ...) +│ │ ├── errors/ # Custom domain exception classes +│ │ ├── ports/ # Abstract interfaces (repositories, storage) +│ │ └── value_objects/ # Immutable value types (CoverageStatus, Role, ...) +│ │ +│ ├── services/ # Business logic layer (~45 services) +│ │ ├── test_service.py +│ │ ├── coverage_service.py +│ │ ├── campaign_service.py +│ │ ├── alert_service.py +│ │ ├── report_service.py +│ │ └── ... +│ │ +│ ├── routers/ # FastAPI route handlers (~28 routers) +│ │ ├── auth.py # /api/v1/auth +│ │ ├── tests.py # /api/v1/tests +│ │ ├── techniques.py # /api/v1/techniques +│ │ ├── campaigns.py # /api/v1/campaigns +│ │ └── ... # one file per module +│ │ +│ ├── models/ # SQLAlchemy ORM model classes +│ │ ├── test.py +│ │ ├── technique.py +│ │ ├── user.py +│ │ └── ... +│ │ +│ ├── schemas/ # Pydantic v2 request/response schemas +│ │ ├── test_schemas.py +│ │ ├── user_schemas.py +│ │ └── ... +│ │ +│ ├── infrastructure/ # Adapters for external systems +│ │ ├── redis_client.py # Redis connection + helpers +│ │ ├── minio_client.py # MinIO / S3 evidence storage +│ │ └── persistence/ # SQLAlchemy session management +│ │ +│ ├── jobs/ # APScheduler background jobs +│ │ ├── mitre_sync.py # Hourly MITRE ATT&CK data sync +│ │ ├── alert_eval.py # Hourly alert rule evaluation +│ │ └── retention.py # Snapshot retention cleanup +│ │ +│ ├── core/ +│ │ ├── config.py # Settings via pydantic-settings / .env +│ │ ├── security.py # JWT creation/validation, password hashing +│ │ └── dependencies.py # FastAPI dependency injection (get_db, get_current_user) +│ │ +│ └── main.py # App factory, router registration, CORS, middleware +│ +├── alembic/ # Migration scripts +│ └── versions/ +├── tests/ # Pytest test suite (367+ tests) +│ ├── test_auth.py +│ ├── test_tests.py +│ └── ... +├── entrypoint.prod.sh # Production startup: run migrations then uvicorn +├── requirements.txt +└── Dockerfile +``` + +--- + +## API URL Structure + +Every API endpoint is prefixed with `/api/v1/`. Examples: + +``` +GET /api/v1/techniques +POST /api/v1/tests +GET /api/v1/dashboard/kpis +POST /api/v1/campaigns/{id}/complete +``` + +The Swagger UI (development only) is available at `http://localhost:8000/docs`. +ReDoc is at `http://localhost:8000/redoc`. +In production (`AEGIS_ENV=production`) both are disabled. + +--- + +## Authentication Flow + +``` +Client Backend Redis + │ │ │ + │ POST /auth/login │ │ + │ (username + password) │ │ + │──────────────────────────>│ │ + │ │ verify password hash │ + │ │ create JWT (jti claim) │ + │ Set-Cookie: aegis_token │ │ + │<──────────────────────────│ │ + │ │ │ + │ GET /api/v1/tests │ │ + │ (Cookie: aegis_token) │ │ + │──────────────────────────>│ │ + │ │ decode JWT │ + │ │ check blacklist (jti) ────>│ + │ │<─────────────────── not found │ + │ │ inject current_user │ + │ 200 OK │ │ + │<──────────────────────────│ │ + │ │ │ + │ POST /auth/logout │ │ + │──────────────────────────>│ │ + │ │ store jti in blacklist ───>│ + │ │ (TTL = token exp) │ + │ Clear cookie │ │ + │<──────────────────────────│ │ +``` + +Alternative: pass token as `Authorization: Bearer ` header +(e.g. for API clients or when cookies are not practical). + +--- + +## Background Jobs (APScheduler) + +Aegis uses APScheduler (in-process) for recurring tasks: + +| Job ID | Schedule | Description | +|--------|----------|-------------| +| `mitre_sync` | Every hour | Fetches latest MITRE ATT&CK STIX data from GitHub, upserts techniques | +| `alert_evaluation` | Every hour | Evaluates all enabled alert rules against current data | +| `snapshot_retention` | Daily | Deletes old snapshots beyond the configured retention window | + +Jobs are registered in `app/jobs/` and started in `main.py` during application startup. +Admin can check job status at `GET /api/v1/system/scheduler-status`. +Manual MITRE sync: `POST /api/v1/system/sync-mitre` (admin only). + +--- + +## Data Flow — Test Execution + +``` +red_tech Backend DB MinIO + │ │ │ │ + │ POST /tests/{id}/ │ │ │ + │ start-execution │ │ │ + │───────────────────────>│ check role (red_tech+) │ │ + │ │ validate state = draft │ │ + │ │ UPDATE state=red_executing│ │ + │ │──────────────────────────>│ │ + │ 200 OK │ │ │ + │<───────────────────────│ │ │ + │ │ │ │ + │ POST /tests/{id}/ │ │ │ + │ evidence │ │ │ + │ (multipart file) │ │ │ + │───────────────────────>│ check role + state │ │ + │ │ upload file ──────────────────────────>│ + │ │ store metadata ───────────>│ │ + │ 201 Created │ │ │ + │<───────────────────────│ │ │ +``` + +--- + +## Production vs Development Differences + +| Feature | Development | Production | +|---------|-------------|------------| +| Swagger UI (`/docs`) | Enabled | Disabled | +| ReDoc (`/redoc`) | Enabled | Disabled | +| Cookie `secure` flag | False (HTTP allowed) | True (HTTPS required) | +| `AEGIS_ENV` | `development` | `production` | +| `SECURE_COOKIES` | `auto` or `false` | `auto` or `true` | +| Debug logging | Verbose | Structured JSON | +| CORS origins | Permissive | Restricted | +| DB migrations | Manual or auto | Auto on startup via `entrypoint.prod.sh` | +| Secret key | Can be default | **Must** be set via `SECRET_KEY` env var | + +See [Deployment-Guide](Deployment-Guide) for full configuration details. +""" + +PAGE_ROLES = """\ +# Roles and Permissions + +Aegis uses Role-Based Access Control (RBAC) with six roles organized in a hierarchy. +Every authenticated request carries the user's role, which is checked against the +required role for each endpoint. + +--- + +## Role Hierarchy + +``` +admin + └─ red_lead / blue_lead + └─ red_tech / blue_tech + └─ viewer +``` + +Higher roles generally include the capabilities of lower roles, with some +team-specific restrictions (e.g. a `red_lead` cannot perform `blue_lead` actions). + +> **Admin bypass**: `admin` ALWAYS passes any role check, regardless of which role +> is required. There is no endpoint an admin cannot call. + +--- + +## Role Descriptions + +### admin + +**Primary use case**: Platform administrator. Full system access. + +**Can do:** +- Everything any other role can do +- Create, update, deactivate, and delete users +- View audit logs (`GET /api/v1/audit-logs`) +- Delete snapshots (`DELETE /api/v1/snapshots/{id}`) +- Delete alert rules +- Trigger MITRE ATT&CK sync (`POST /api/v1/system/sync-mitre`) +- Manage webhooks (`GET/POST/PATCH/DELETE /api/v1/webhooks`) — **admin only** +- Set data classification on tests (`PATCH /api/v1/tests/{id}/classification`) +- View scheduler status (`GET /api/v1/system/scheduler-status`) +- Configure SSO (`PUT /api/v1/sso/config`) +- Manage data sources (`POST/PATCH/DELETE /api/v1/data-sources`) +- Complete campaigns (along with red_lead) + +**Cannot do:** +- Be locked out — bypasses all role checks by design + +--- + +### red_lead + +**Primary use case**: Red Team Lead. Manages the offensive side of operations. + +**Can do:** +- Create, update, and delete tests (in `draft` or `rejected` state) +- Create campaigns and complete them (`POST /campaigns/{id}/complete`) +- Create test templates +- Create and manage playbooks and lessons +- Validate the red side of a test (`POST /tests/{id}/validate-red`) +- Manage attack paths (including delete) +- Create alert rules +- Generate professional reports (PDF, DOCX, HTML) +- Start test execution +- Upload red evidence +- Submit red assessment +- All viewer capabilities + +**Cannot do:** +- Access audit logs +- Perform blue-side actions (validate-blue, submit-blue, update blue fields) +- Delete snapshots +- Sync MITRE data +- Access or manage webhooks +- Set data classification + +--- + +### blue_lead + +**Primary use case**: Blue Team Lead. Manages the defensive side of operations. + +**Can do:** +- Evaluate and validate the blue side of a test (`POST /tests/{id}/validate-blue`) +- Validate blue side (`POST /tests/{id}/validate-blue`) +- Manage detection assets and detection rules +- Create playbooks and lessons +- Generate a revalidation queue +- Manage ownership of assets +- Create alert rules +- Generate professional reports (PDF, DOCX, HTML) +- Upload blue evidence (in `blue_evaluating` state) +- Submit blue assessment +- All viewer capabilities + +**Cannot do:** +- Complete campaigns (red_lead + admin only) +- Start test execution (`POST /tests/{id}/start-execution`) +- Validate the red side +- Access audit logs +- Delete snapshots +- Sync MITRE data +- Access or manage webhooks + +--- + +### red_tech + +**Primary use case**: Red Team Technician. Executes attacks. + +**Can do:** +- Start test execution (`POST /tests/{id}/start-execution`) — moves `draft` → `red_executing` +- Update red-side fields (`PATCH /tests/{id}/red`): `tool_used`, `command_executed`, `output_observed`, `red_notes`, etc. +- Upload red evidence files +- Submit red assessment (`POST /tests/{id}/submit-red`) — moves `red_executing` → `blue_evaluating` +- Pause and resume test timers +- Create worklogs for tests +- View all read-only data (same as viewer) + +**Cannot do:** +- Create new tests +- Create campaigns +- Manage playbooks or lessons +- Access webhooks, audit logs, or admin endpoints +- Perform blue-side actions +- Validate either side +- Generate professional reports + +--- + +### blue_tech + +**Primary use case**: Blue Team Technician. Evaluates detection. + +**Can do:** +- Update blue-side fields (`PATCH /tests/{id}/blue`): `detection_result` (detected/not_detected/partial), `detection_notes`, `alert_fired`, `response_action`, etc. +- Upload blue evidence files (only while test is in `blue_evaluating` state) +- Submit blue assessment (`POST /tests/{id}/submit-blue`) — moves `blue_evaluating` → `in_review` +- Evaluate detection rules (`POST /api/v1/detection-rules/evaluate`) +- Create detection validations +- Pause and resume test timers +- Create worklogs for tests +- View all read-only data (same as viewer) + +**Cannot do:** +- Start test execution +- Submit red assessment +- Validate either side +- Create tests, campaigns, playbooks, or lessons +- Access webhooks, audit logs, or admin endpoints +- Generate professional reports + +--- + +### viewer + +**Primary use case**: Stakeholder, auditor, or report consumer. Read-only access plus report generation. + +**Can do:** +- Read all entities: tests, techniques, campaigns, playbooks, lessons, coverage, snapshots, alerts, dashboards, detection lifecycle, scores, heatmap, threat actors, compliance, analytics +- Generate professional reports: PDF, DOCX, HTML (all report types) +- Mark own notifications as read + +**Cannot do:** +- Create or modify any entity (no POST/PATCH/DELETE on data entities) +- Upload evidence +- Change test state +- Manage users, webhooks, alert rules, playbooks, campaigns, or any configuration + +--- + +## Access Matrix + +The table below shows key endpoints and which roles can call them. +✅ = allowed, ❌ = forbidden, * = with restrictions (see notes) + +| Endpoint | admin | red_lead | blue_lead | red_tech | blue_tech | viewer | +|----------|-------|----------|-----------|----------|-----------|--------| +| POST /tests | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | +| PATCH /tests/{id} (general) | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | +| POST /tests/{id}/start-execution | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | +| PATCH /tests/{id}/red | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | +| POST /tests/{id}/submit-red | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | +| PATCH /tests/{id}/blue | ✅ | ❌ | ✅ | ❌ | ✅ | ❌ | +| POST /tests/{id}/submit-blue | ✅ | ❌ | ✅ | ❌ | ✅ | ❌ | +| POST /tests/{id}/validate-red | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | +| POST /tests/{id}/validate-blue | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | +| POST /tests/{id}/reopen | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | +| POST /tests/{id}/pause-timer | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | +| PATCH /tests/{id}/classification | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | +| POST /campaigns | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | +| POST /campaigns/{id}/complete | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | +| POST /campaigns/from-threat-actor/{id} | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | +| POST /knowledge/playbooks | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | +| GET /reports/generate/* | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | +| GET /audit-logs | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | +| ALL /webhooks | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | +| DELETE /snapshots/{id} | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | +| POST /system/sync-mitre | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | +| PUT /sso/config | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | +| PATCH /data-sources | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | +| GET /users (list) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | +| POST /users | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | +| PATCH /alerts/rules | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | +| POST /alerts/evaluate | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | +| GET /scores/organization | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| PATCH /scores/config | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | + +--- + +## Notes on Role Enforcement + +1. **JWT carries the role**: The user's role is embedded in the JWT payload and + re-verified against the database on every request. +2. **Admin bypass is unconditional**: The `require_role()` dependency immediately + returns if `current_user.role == "admin"`. +3. **Resource ownership**: Some endpoints additionally check that the requesting + user owns the resource (e.g. API keys, worklogs, notifications). Admins bypass + ownership checks too. +4. **must_change_password**: If this flag is set on the user record, all endpoints + return `403 PASSWORD_CHANGE_REQUIRED` until the password is changed via + `POST /api/v1/auth/change-password`. +""" + +PAGE_TEST_LIFECYCLE = """\ +# Test Lifecycle + +Every test in Aegis follows a structured state machine from creation to final validation. +This page documents every state, transition, actor, and field update rule. + +--- + +## State Diagram + +``` + ┌─────────┐ + │ draft │◄──────────────────────────┐ + └────┬────┘ │ + │ POST /start-execution │ + │ (red_tech, red_lead, admin) │ POST /reopen + ▼ │ (leads, admin) + ┌─────────────────┐ │ + │ red_executing │ │ + └────────┬────────┘ │ + │ POST /submit-red │ + │ (red_tech, red_lead, admin) │ + ▼ │ + ┌─────────────────────┐ │ + │ blue_evaluating │ │ + └────────┬────────────┘ │ + │ POST /submit-blue │ + │ (blue_tech, blue_lead, admin) │ + ▼ │ + ┌───────────┐ │ + │ in_review │ │ + └─────┬─────┘ │ + │ Both validate (approved) │ + │ │ + ┌──────────┴──────────┐ │ + │ Either rejects │ │ + ▼ ▼ │ + ┌──────────┐ ┌───────────┐ │ + │ rejected │─────────┤ validated │ │ + └──────────┘ └───────────┘ │ + │ │ + └───────────────────────────────────────────┘ +``` + +--- + +## States Reference + +### draft + +- **Description**: Initial state. Test has been defined but not yet executed. +- **Who creates**: red_lead, blue_lead, admin (POST /api/v1/tests) +- **Who can edit general fields**: red_lead, blue_lead, admin (PATCH /api/v1/tests/{id}) +- **Available actions**: + - Edit test metadata (title, description, technique_id, objective, target_environment, etc.) + - Add to campaigns + - Attach red evidence (even before execution, for pre-work files) +- **Cannot do**: upload blue evidence, start timer + +**Create payload example:** +```json +{ + "title": "T1059.001 PowerShell cradle download", + "technique_id": "T1059.001", + "objective": "Verify AMSI bypass detection", + "target_environment": "Windows 10 workstation", + "severity": "high", + "campaign_id": "optional-uuid" +} +``` + +--- + +### red_executing + +- **Description**: Red team is actively executing the attack. +- **Trigger**: POST /api/v1/tests/{id}/start-execution +- **Who can trigger**: red_tech, red_lead, admin +- **Timer starts** automatically when entering this state. +- **Who updates red fields**: red_tech, red_lead, admin (PATCH /api/v1/tests/{id}/red) + +**Red fields (PATCH /tests/{id}/red):** +```json +{ + "tool_used": "Cobalt Strike 4.8", + "command_executed": "powershell -nop -w hidden -enc ...", + "output_observed": "Beacon established on 10.0.0.5", + "red_notes": "Used staged payload to bypass AV", + "attacker_ip": "10.10.10.100", + "target_ip": "10.0.0.5", + "start_time": "2024-03-15T10:00:00Z", + "end_time": "2024-03-15T10:15:00Z" +} +``` + +**Evidence upload (team=red):** +```http +POST /api/v1/tests/{id}/evidence +Content-Type: multipart/form-data + +file= +team=red +description=PowerShell execution screenshot +``` + +--- + +### blue_evaluating + +- **Description**: Blue team is evaluating what they detected (or didn't). +- **Trigger**: POST /api/v1/tests/{id}/submit-red +- **Who can trigger**: red_tech, red_lead, admin +- **Who updates blue fields**: blue_tech, blue_lead, admin (PATCH /api/v1/tests/{id}/blue) + +**Blue fields (PATCH /tests/{id}/blue):** +```json +{ + "detection_result": "detected", + "detection_notes": "Windows Defender ATP alert fired: Suspicious PowerShell", + "alert_fired": true, + "alert_name": "ASR rule: Block Office apps from creating child processes", + "response_action": "Endpoint isolated automatically", + "time_to_detect_seconds": 47, + "blue_notes": "AMSI telemetry captured full script content" +} +``` + +`detection_result` values: +- `detected` — blue team observed the attack in time +- `not_detected` — attack was not detected +- `partial` — partial detection (some indicators observed but not full alert) + +**Evidence upload (team=blue):** +```http +POST /api/v1/tests/{id}/evidence +Content-Type: multipart/form-data + +file= +team=blue +description=SIEM alert screenshot +``` + +--- + +### in_review + +- **Description**: Both leads must independently validate their side. +- **Trigger**: POST /api/v1/tests/{id}/submit-blue +- **Who can trigger**: blue_tech, blue_lead, admin +- **No field updates allowed** while in review. +- **Evidence is locked** — no new evidence uploads. + +**Red validation (red_lead, admin):** +```http +POST /api/v1/tests/{id}/validate-red +{"red_validation_status": "approved", "red_validation_notes": "Execution was clean"} +``` + +**Blue validation (blue_lead, admin):** +```http +POST /api/v1/tests/{id}/validate-blue +{"blue_validation_status": "approved", "blue_validation_notes": "Detection confirmed"} +``` + +Both validations can be `approved` or `rejected`. + +--- + +### validated + +- **Description**: Both leads approved. Test is complete and results are official. +- **Trigger**: Both red_validation_status and blue_validation_status = `approved` +- **Effects**: + - Technique coverage status is recalculated + - Webhook events fired (if configured): `test.validated` + - Score may be updated + - Snapshot data refreshed +- **After validation**: Leads and admin can still edit the `remediation` fields: + ```http + PATCH /api/v1/tests/{id}/remediation + { + "remediation_status": "in_progress", + "remediation_notes": "SIEM rule deployed to block this technique", + "remediation_owner": "blue-lead-user-uuid" + } + ``` + +--- + +### rejected + +- **Description**: At least one lead rejected their side. +- **Trigger**: Either validate-red or validate-blue returns `rejected` +- **Recovery**: POST /api/v1/tests/{id}/reopen (leads, admin) sends back to `draft` + +--- + +## Evidence Rules Summary + +| State | Red evidence | Blue evidence | +|-------|-------------|---------------| +| draft | Upload allowed | Not allowed | +| red_executing | Upload allowed | Not allowed | +| blue_evaluating | Not allowed | Upload allowed | +| in_review | **Locked** | **Locked** | +| validated | **Locked** | **Locked** | +| rejected | **Locked** | **Locked** | + +--- + +## Timer + +The test timer measures elapsed execution time. + +| Action | Endpoint | Allowed roles | +|--------|----------|---------------| +| Pause timer | POST /tests/{id}/pause-timer | All except viewer | +| Resume timer | POST /tests/{id}/resume-timer | All except viewer | + +The timer automatically starts when entering `red_executing`. + +--- + +## Timeline and Audit Trail + +Every state change is recorded in the timeline: +```http +GET /api/v1/tests/{id}/timeline +``` + +Response includes entries for: +- State transitions (from_state, to_state, actor, timestamp) +- Validation actions (approved/rejected, actor, timestamp) +- Evidence uploads (team, filename, actor, timestamp) +- Timer pauses/resumes +- Worklog entries + +--- + +## Retest Chain + +When a test is a retest of a previous (failed) test, it is linked via `retest_of`: +```json +{"retest_of": "previous-test-uuid"} +``` + +Navigate the full chain: +```http +GET /api/v1/tests/{id}/retest-chain +``` + +Returns an ordered list of all tests in the chain, oldest first. +""" + +PAGE_MITRE = """\ +# MITRE ATT&CK Coverage + +Aegis is built around the MITRE ATT&CK framework. This page explains how the coverage +system works, how techniques are scored, and how the data is maintained. + +--- + +## What is MITRE ATT&CK? + +MITRE ATT&CK is a globally-accessible knowledge base of adversary tactics and techniques +based on real-world observations. It provides a common language for describing attacker +behavior, allowing Red and Blue teams to align their work. + +- Official reference: https://attack.mitre.org/ +- Aegis uses the **Enterprise** matrix +- Techniques are identified by IDs like `T1059` (parent) and `T1059.001` (sub-technique) + +--- + +## Tactics (14) + +| # | Tactic ID | Tactic Name | Description | +|---|-----------|-------------|-------------| +| 1 | TA0043 | Reconnaissance | Gathering information before attack | +| 2 | TA0042 | Resource Development | Preparing attack resources | +| 3 | TA0001 | Initial Access | Getting into the target environment | +| 4 | TA0002 | Execution | Running adversary-controlled code | +| 5 | TA0003 | Persistence | Maintaining foothold | +| 6 | TA0004 | Privilege Escalation | Gaining higher-level permissions | +| 7 | TA0005 | Defense Evasion | Avoiding detection | +| 8 | TA0006 | Credential Access | Stealing credentials | +| 9 | TA0007 | Discovery | Exploring the environment | +| 10 | TA0008 | Lateral Movement | Moving through the network | +| 11 | TA0009 | Collection | Gathering data of interest | +| 12 | TA0011 | Command and Control | Communicating with compromised systems | +| 13 | TA0010 | Exfiltration | Stealing data out of the environment | +| 14 | TA0040 | Impact | Manipulating, interrupting, or destroying systems | + +--- + +## Coverage Statuses + +Each technique has a `coverage_status` field: + +| Status | Meaning | +|--------|---------| +| `not_covered` | No validated test exists for this technique | +| `partial` | At least one test exists but not all are validated, OR detection was partial | +| `validated` | At least one test is in `validated` state with both leads approved | + +Coverage is stored in the `techniques` table and recalculated automatically when: +- A test reaches `validated` state +- A test is reopened (back to draft from validated/rejected) +- MITRE sync runs and adds/removes techniques + +--- + +## Coverage Scoring + +Aegis calculates a **weighted coverage score** (0–100) for each technique and for the +organization overall. The formula combines multiple signals: + +### Default weights (configurable) + +| Signal | Default weight | Description | +|--------|---------------|-------------| +| Tests | 40% | Number and outcome of validated tests | +| Detection rules | 30% | Detection rules linked to the technique | +| D3FEND | 10% | Defensive countermeasures mapped via MITRE D3FEND | +| Recency | 10% | How recently the technique was last tested | +| Severity | 10% | Technique severity/impact factor | + +### Configuring weights + +Only admins can change scoring weights: +```http +PATCH /api/v1/scores/config +{ + "test_weight": 0.40, + "detection_rule_weight": 0.30, + "d3fend_weight": 0.10, + "recency_weight": 0.10, + "severity_weight": 0.10 +} +``` + +Weights must sum to 1.0. + +### Score endpoints + +| Endpoint | Description | +|----------|-------------| +| GET /api/v1/scores/organization | Overall organization coverage score | +| GET /api/v1/scores/techniques/{id} | Score for a specific technique | +| GET /api/v1/scores/by-tactic | Scores aggregated by tactic | + +--- + +## MITRE Data Sync + +Aegis maintains its own copy of the MITRE ATT&CK technique database. + +**Automatic sync**: Runs hourly via APScheduler. Fetches the latest STIX data from +the official MITRE ATT&CK GitHub repository and upserts all techniques. + +**Manual sync** (admin only): +```http +POST /api/v1/system/sync-mitre +``` + +**What syncs:** +- Technique ID, name, description +- Tactic associations +- Sub-technique relationships +- Deprecation/revocation status +- Platform tags (Windows, Linux, macOS, Cloud, etc.) + +**New techniques** added by MITRE automatically appear as `not_covered` in Aegis. +**Deprecated techniques** are marked accordingly but retained for historical test data. + +--- + +## Technique Review + +When your infrastructure changes (new tools, new SIEM rules), techniques may need +re-testing to confirm coverage is still accurate. + +**Mark a technique for review** (leads, admin): +```http +PATCH /api/v1/techniques/{id}/review +{"needs_review": true, "review_reason": "SIEM rules rebuilt after migration"} +``` + +This triggers the revalidation queue in the detection lifecycle module. + +--- + +## Heatmap + +The heatmap visualizes coverage across all tactics and techniques: + +```http +GET /api/v1/heatmap +``` + +Returns a matrix organized by tactic, with each technique cell showing: +- `coverage_status` +- `score` +- `test_count` +- `last_tested` date + +--- + +## D3FEND Mappings + +MITRE D3FEND is a knowledge graph of cybersecurity countermeasures. +Aegis integrates D3FEND mappings to show which defensive techniques apply to each +ATT&CK technique. + +```http +GET /api/v1/techniques/{mitre_id}/d3fend +``` + +Returns a list of D3FEND techniques (countermeasures) mapped to the ATT&CK technique, +with descriptions and implementation guidance. + +--- + +## Risk Profiles + +The risk module assesses how dangerous each uncovered technique is: + +| Endpoint | Description | +|----------|-------------| +| GET /api/v1/risk/profiles | Risk profile per technique | +| GET /api/v1/risk/matrix | Risk matrix (severity × coverage) | +| GET /api/v1/risk/summary | Aggregate risk summary | +| GET /api/v1/risk/top | Top N highest-risk uncovered techniques | +| GET /api/v1/risk/recommendations | Prioritized remediation recommendations | +| POST /api/v1/risk/compute | Trigger manual risk recomputation | + +**Top uncovered techniques:** +```http +GET /api/v1/risk/top?limit=10 +``` + +Returns the 10 highest-priority uncovered techniques based on: +- MITRE technique severity/prevalence +- Threat actor association (if the technique is used by tracked threat actors) +- Current coverage gap size +""" + +PAGE_API_REF = """\ +# API Reference + +All endpoints are prefixed with `/api/v1/`. Authentication via cookie `aegis_token` or +`Authorization: Bearer `. Role abbreviations: A=admin, RL=red_lead, BL=blue_lead, +RT=red_tech, BT=blue_tech, V=viewer, Any=all authenticated. + +--- + +## Auth — /api/v1/auth + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| POST | /auth/login | Public | Login with username+password (form-encoded). Sets `aegis_token` cookie. | +| POST | /auth/logout | Any | Revokes token (adds jti to Redis blacklist). Clears cookie. | +| GET | /auth/me | Any | Returns current user profile + role. | +| POST | /auth/change-password | Any | Change own password. Required if `must_change_password=true`. | + +--- + +## Users — /api/v1/users + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | /users | A | List all users (paginated). | +| POST | /users | A | Create a new user. Sets `must_change_password=true`. | +| GET | /users/{id} | A | Get user by ID. | +| PATCH | /users/{id} | A | Update user (role, is_active, display_name, etc.). | +| DELETE | /users/{id} | A | Deactivate (soft-delete) a user. | +| GET | /users/me | Any | Get own profile. | +| PATCH | /users/me/preferences | Any | Update own UI preferences (theme, notifications, etc.). | + +--- + +## Techniques — /api/v1/techniques + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | /techniques | Any | List techniques. Query: tactic_id, platform, coverage_status, search. | +| GET | /techniques/{mitre_id} | Any | Get technique by MITRE ID (e.g. T1059.001). | +| POST | /techniques | A | Create a custom technique (non-MITRE). | +| PATCH | /techniques/{mitre_id} | A | Update technique metadata. | +| PATCH | /techniques/{mitre_id}/review | RL,BL,A | Mark technique as needing review. | +| GET | /techniques/{mitre_id}/d3fend | Any | D3FEND countermeasure mappings for this technique. | + +--- + +## Tests — /api/v1/tests + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | /tests | Any | List tests. Filters: state, technique_id, campaign_id, assigned_to. | +| POST | /tests | RL,BL,A | Create a new test in draft state. | +| POST | /tests/from-template | RL,BL,A | Create test(s) from a saved template. | +| GET | /tests/{id} | Any | Get full test detail including all fields. | +| PATCH | /tests/{id} | RL,BL,A | Update general fields (title, objective, technique, etc.). Draft/rejected only. | +| PATCH | /tests/{id}/classification | A | Set data_classification (confidential, restricted, etc.). | +| PATCH | /tests/{id}/red | RL,RT,A | Update red-side fields. State: red_executing. | +| PATCH | /tests/{id}/blue | BL,BT,A | Update blue-side fields. State: blue_evaluating. | +| POST | /tests/{id}/start-execution | RL,RT,A | Transition draft → red_executing. | +| POST | /tests/{id}/submit-red | RL,RT,A | Transition red_executing → blue_evaluating. | +| POST | /tests/{id}/submit-blue | BL,BT,A | Transition blue_evaluating → in_review. | +| POST | /tests/{id}/validate-red | RL,A | Approve/reject red side. State: in_review. | +| POST | /tests/{id}/validate-blue | BL,A | Approve/reject blue side. State: in_review. | +| POST | /tests/{id}/reopen | RL,BL,A | Transition rejected → draft. | +| POST | /tests/{id}/pause-timer | All except V | Pause elapsed time counter. | +| POST | /tests/{id}/resume-timer | All except V | Resume elapsed time counter. | +| PATCH | /tests/{id}/remediation | RL,BL,A | Update remediation status/notes (any state post-review). | +| GET | /tests/{id}/timeline | Any | Full audit trail of state changes and actions. | +| GET | /tests/{id}/retest-chain | Any | All tests in this retest lineage. | + +--- + +## Evidence — /api/v1/tests/{id}/evidence & /api/v1/evidence + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| POST | /tests/{id}/evidence | Role-dependent* | Upload evidence file. team=red or team=blue. | +| GET | /tests/{id}/evidence | Any | List all evidence files for a test. | +| GET | /evidence/{id} | Any | Get evidence metadata. | +| GET | /evidence/{id}/download | Any | Download evidence file from MinIO (signed URL or stream). | +| DELETE | /evidence/{id} | Own or RL,BL,A | Delete evidence file. | + +*red evidence: RL, RT, A | blue evidence: BL, BT, A. State restrictions apply (see [Test-Lifecycle](Test-Lifecycle)). + +--- + +## Campaigns — /api/v1/campaigns + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | /campaigns | Any | List campaigns. Filters: status, type, date range. | +| POST | /campaigns | RL,BL,A | Create a new campaign. | +| GET | /campaigns/{id} | Any | Get campaign detail. | +| PATCH | /campaigns/{id} | RL,BL,A | Update campaign metadata. | +| POST | /campaigns/{id}/tests | RL,BL,A | Add a test to a campaign. | +| DELETE | /campaigns/{id}/tests/{ctid} | RL,BL,A | Remove a test from a campaign. | +| POST | /campaigns/{id}/activate | RL,BL,A | Transition draft → active. | +| POST | /campaigns/{id}/complete | RL,A | Transition active → completed. | +| GET | /campaigns/{id}/progress | Any | Count validated/total tests in campaign. | +| POST | /campaigns/from-threat-actor/{actor_id} | RL,BL,A | Auto-create campaign + tests from threat actor profile. | +| PATCH | /campaigns/{id}/schedule | RL,BL,A | Set start_date / end_date. | +| GET | /campaigns/{id}/history | Any | Campaign state change history. | + +--- + +## Attack Paths — /api/v1/attack-paths + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | /attack-paths | Any | List attack paths. | +| POST | /attack-paths | All except V | Create an attack path (sequence of techniques). | +| GET | /attack-paths/{id} | Any | Get attack path detail. | +| PATCH | /attack-paths/{id} | All except V | Update attack path metadata. | +| DELETE | /attack-paths/{id} | RL,BL,A | Delete attack path. | +| POST | /attack-paths/{id}/steps | All except V | Add a step (technique) to attack path. | +| DELETE | /attack-paths/{id}/steps/{sid} | All except V | Remove a step. | +| POST | /attack-paths/{id}/executions | All except V | Start an execution of the attack path. | +| POST | /executions/{id}/start | All except V | Begin execution. | +| POST | /executions/{id}/steps/{sid} | All except V | Record step outcome. | +| POST | /executions/{id}/complete | All except V | Mark execution complete. | +| POST | /executions/{id}/abort | RL,BL,A | Abort execution. | + +--- + +## Knowledge — /api/v1/knowledge + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | /knowledge/playbooks | Any | List playbooks. Filter by technique, type. | +| POST | /knowledge/playbooks | RL,BL,A | Create a playbook. | +| GET | /knowledge/playbooks/{id} | Any | Get playbook + versions list. | +| PATCH | /knowledge/playbooks/{id} | RL,BL,A | Update playbook (creates version snapshot). | +| DELETE | /knowledge/playbooks/{id} | RL,BL,A | Delete playbook. | +| GET | /knowledge/playbooks/{id}/versions | Any | List all versions of a playbook. | +| POST | /knowledge/playbooks/{id}/restore/{version} | RL,BL,A | Restore a previous version. | +| GET | /knowledge/lessons | Any | List lessons learned. Filter by severity, technique. | +| POST | /knowledge/lessons | RL,BL,A | Create a lesson learned. | +| GET | /knowledge/lessons/{id} | Any | Get lesson detail. | +| PATCH | /knowledge/lessons/{id} | RL,BL,A | Update lesson. | +| DELETE | /knowledge/lessons/{id} | RL,BL,A | Delete lesson. | +| GET | /knowledge/stats | Any | Summary: playbooks by type, lessons by severity. | + +--- + +## Ownership — /api/v1/ownership + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | /ownership/assignments | Any | Technique-to-owner assignments. | +| POST | /ownership/assignments | RL,BL,A | Assign technique ownership. | +| PATCH | /ownership/assignments/{id} | RL,BL,A | Update assignment. | +| DELETE | /ownership/assignments/{id} | RL,BL,A | Remove assignment. | +| GET | /ownership/assets | Any | Asset ownership list. | +| POST | /ownership/assets | BL,A | Create asset ownership record. | +| GET | /ownership/revalidation-queue | BL,A | Techniques requiring revalidation. | +| POST | /ownership/revalidation-queue/generate | BL,A | Generate revalidation queue from infrastructure changes. | + +--- + +## Risk — /api/v1/risk + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| POST | /risk/compute | RL,BL,A | Trigger risk recomputation. | +| GET | /risk/profiles | Any | Risk profile per technique. | +| GET | /risk/matrix | Any | Risk matrix (impact × coverage). | +| GET | /risk/summary | Any | Aggregate risk numbers. | +| GET | /risk/recommendations | Any | Prioritized list of techniques to test next. | +| GET | /risk/top | Any | Top N highest-risk uncovered techniques. ?limit=N | + +--- + +## Alerts — /api/v1/alerts + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| POST | /alerts/evaluate | RL,BL,A | Manually trigger evaluation of all enabled rules. | +| GET | /alerts/summary | Any | Alert counts by status, severity, rule type. | +| GET | /alerts | Any | List alert instances. Filters: status, severity, rule_type. | +| GET | /alerts/{id} | Any | Get alert instance detail. | +| POST | /alerts/{id}/acknowledge | RL,BL,A | Acknowledge an alert. | +| POST | /alerts/{id}/resolve | RL,BL,A | Resolve an alert. | +| POST | /alerts/{id}/dismiss | RL,BL,A | Dismiss an alert. | +| GET | /alerts/rules | Any | List alert rules. | +| POST | /alerts/rules | RL,BL,A | Create an alert rule. | +| GET | /alerts/rules/{id} | Any | Get rule detail. | +| PATCH | /alerts/rules/{id} | RL,BL,A | Update rule. | +| DELETE | /alerts/rules/{id} | A | Delete rule. | + +--- + +## Dashboard — /api/v1/dashboard + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | /dashboard/kpis | Any | Key metrics: total techniques, covered %, tests, campaigns. | +| GET | /dashboard/executive | Any | Narrative executive summary. | +| GET | /dashboard/coverage-by-tactic | Any | Coverage breakdown per tactic. | +| GET | /dashboard/posture-history | Any | Historical coverage trend data. | +| GET | /dashboard/activity | Any | Recent activity feed (tests, campaigns, validations). | +| POST | /dashboard/posture-snapshot | RL,BL,A | Create an immediate posture snapshot. | + +--- + +## Snapshots — /api/v1/snapshots + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | /snapshots | Any | List snapshots (paginated). | +| POST | /snapshots | RL,BL,A | Create a manual snapshot. | +| GET | /snapshots/{id} | Any | Get snapshot detail. | +| DELETE | /snapshots/{id} | A | Delete a snapshot. | +| GET | /snapshots/evolution | Any | Trend data: coverage over time. | +| GET | /snapshots/compare | Any | Diff two snapshots. Query: ?a=&b= | + +--- + +## Reports — /api/v1/reports + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | /reports/coverage-summary | Any | Coverage data as JSON. | +| GET | /reports/coverage-csv | Any | Coverage data as CSV download. | +| GET | /reports/test-results | Any | Test results data as JSON. | +| GET | /reports/remediation-status | Any | Remediation tracking data. | +| GET | /reports/generate/purple-campaign/{id} | RL,BL,A,V | Full campaign report. ?format=pdf|docx|html | +| GET | /reports/generate/coverage-summary | RL,BL,A,V | Coverage status report. | +| GET | /reports/generate/executive-summary | RL,BL,A,V | Executive briefing document. | +| GET | /reports/generate/quarterly-summary | RL,BL,A,V | Quarterly review report. | +| GET | /reports/generate/technique/{id} | RL,BL,A,V | Per-technique detail report. | + +--- + +## Detection Lifecycle — /api/v1/detection-lifecycle + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | /detection-lifecycle/assets | Any | List detection assets. | +| POST | /detection-lifecycle/assets | BL,A | Create a detection asset. | +| GET | /detection-lifecycle/assets/{id} | Any | Get asset detail. | +| PATCH | /detection-lifecycle/assets/{id} | BL,A | Update asset. | +| DELETE | /detection-lifecycle/assets/{id} | BL,A | Delete asset. | +| POST | /detection-lifecycle/assets/{id}/techniques/{tid} | BL,A | Link asset to technique. | +| DELETE | /detection-lifecycle/assets/{id}/techniques/{tid} | BL,A | Unlink asset from technique. | +| GET | /detection-lifecycle/validations | Any | List detection validations. | +| POST | /detection-lifecycle/validations | BL,A | Create a validation record. | +| POST | /detection-lifecycle/validations/{id}/invalidate | BL,A | Invalidate (revoke) a validation. | +| GET | /detection-lifecycle/infrastructure-changes | Any | List infrastructure change records. | +| POST | /detection-lifecycle/infrastructure-changes | BL,A | Record an infrastructure change. | +| GET | /detection-lifecycle/dashboard | Any | Detection confidence per technique. | + +--- + +## Detection Rules — /api/v1/detection-rules + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | /detection-rules | Any | List detection rules. | +| POST | /detection-rules | BL,A | Create detection rule. | +| GET | /detection-rules/{id} | Any | Get rule detail. | +| PATCH | /detection-rules/{id} | BL,A | Update rule. | +| DELETE | /detection-rules/{id} | BL,A | Delete rule. | +| POST | /detection-rules/evaluate | BL,BT,A | Record rule triggered/not-triggered for a test. | + +--- + +## Test Templates — /api/v1/test-templates + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | /test-templates | Any | List templates. | +| POST | /test-templates | RL,BL,A | Create template from existing test or from scratch. | +| GET | /test-templates/{id} | Any | Get template detail. | +| PATCH | /test-templates/{id} | RL,BL,A | Update template. | +| DELETE | /test-templates/{id} | RL,BL,A | Delete template. | + +--- + +## Webhooks — /api/v1/webhooks (admin only) + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | /webhooks | A | List configured webhooks. | +| POST | /webhooks | A | Create a webhook (URL, events, secret). | +| GET | /webhooks/{id} | A | Get webhook detail. | +| PATCH | /webhooks/{id} | A | Update webhook. | +| DELETE | /webhooks/{id} | A | Delete webhook. | +| POST | /webhooks/{id}/test | A | Send a test event to the webhook URL. | + +Webhook events: `test.validated`, `test.state_changed`, `campaign.completed`, +`alert.fired`, `coverage.changed`. + +SSRF protection: webhook URLs are validated against private IP ranges before saving. + +--- + +## API Keys — /api/v1/api-keys + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | /api-keys | Any | List own API keys. | +| POST | /api-keys | Any | Create a new API key. Scope: read/write/admin. | +| DELETE | /api-keys/{id} | Own or A | Revoke an API key. | + +Keys are prefixed `aegis_` and passed as `Authorization: Bearer aegis_`. + +--- + +## SSO — /api/v1/sso + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | /sso/status | Any | Whether SSO is configured and enabled. | +| PUT | /sso/config | A | Configure SAML 2.0 SSO settings. | +| GET | /sso/login | Public | Initiate SAML login redirect. | +| POST | /sso/callback | Public | SAML assertion callback. | +| GET | /sso/metadata | Public | Service provider metadata XML. | + +--- + +## Other Modules + +| Module | Path prefix | Auth | Notes | +|--------|-------------|------|-------| +| Notifications | /notifications | Any (own) | GET list, PATCH mark-read | +| Audit Logs | /audit-logs | A | GET with filters | +| Jira | /jira | RL,BL,A | sync, issues | +| Threat Actors | /threat-actors | Any GET, RL,BL,A manage | | +| Compliance | /compliance | Any | frameworks, controls, gaps | +| Analytics | /analytics | Any | trend analytics | +| Heatmap | /heatmap | Any | Coverage matrix | +| Scores | /scores | Any GET; A for config PATCH | | +| Worklogs | /worklogs | Any create; own or A to read | | +| OSINT | /osint | RL,BL,A | | +| Data Sources | /data-sources | Any GET; A manage | | +| System | /system | A (scheduler, sync); Public (health) | | +""" + +PAGE_AUTH_SECURITY = """\ +# Authentication and Security + +This page covers the full authentication model, token management, API key usage, +SSO configuration, and security controls in Aegis. + +--- + +## Authentication Methods + +Aegis supports two authentication methods, checked in order: + +### 1. HttpOnly Cookie (Primary — Browser Use) + +After a successful login, the server sets: + +``` +Set-Cookie: aegis_token=; HttpOnly; SameSite=Strict; Path=/; [Secure in prod] +``` + +The browser automatically sends this cookie on every request. +**This is the recommended method for the frontend.** + +### 2. Authorization: Bearer Header (API / Machine Use) + +```http +Authorization: Bearer +``` + +Use this for: +- API integrations +- Scripts and CI/CD pipelines +- When cookies are not available (e.g. cross-origin API calls) + +--- + +## JWT Token + +### Structure + +The JWT payload contains: +```json +{ + "sub": "user-uuid", + "role": "red_lead", + "jti": "unique-token-id", + "exp": 1710000000, + "iat": 1709996400 +} +``` + +### Expiry + +Configurable via environment variable: +``` +ACCESS_TOKEN_EXPIRE_MINUTES=480 # 8 hours default +``` + +### Token Revocation (Blacklist) + +On logout, the token's `jti` is stored in Redis with TTL = remaining token lifetime: +``` +Redis key: blacklist:{jti} +Value: "1" +TTL: +``` + +Every authenticated request checks Redis for the `jti`. If found → 401 Unauthorized. + +--- + +## Cookie Security + +| Setting | Development | Production | +|---------|-------------|------------| +| `HttpOnly` | True | True | +| `SameSite` | Strict | Strict | +| `Secure` | False | True | +| `Path` | / | / | + +**`SECURE_COOKIES` environment variable:** +- `auto` (default): `Secure=True` when `AEGIS_ENV=production` +- `true`: Always set `Secure` flag +- `false`: Never set `Secure` flag (development only) + +--- + +## Password Policy + +- Minimum length: **12 characters** +- Must contain: uppercase, lowercase, digit, and special character +- Enforced at account creation and password change +- Passwords are hashed with **bcrypt** (work factor 12) + +### must_change_password + +When an admin creates a user, `must_change_password` is set to `True`. +Until the user changes their password, **every endpoint** returns: +```json +{"detail": "PASSWORD_CHANGE_REQUIRED"} +``` +with status `403`, **except**: +- `GET /api/v1/auth/me` +- `POST /api/v1/auth/change-password` + +Change password: +```http +POST /api/v1/auth/change-password +Content-Type: application/json + +{ + "current_password": "admin-set-password", + "new_password": "MyNewSecurePassword123!" +} +``` + +--- + +## API Keys + +API keys provide long-lived credentials for machine-to-machine access without +session management. + +### Creating a Key + +```http +POST /api/v1/api-keys +Content-Type: application/json + +{ + "name": "CI Pipeline Key", + "scope": "read", + "expires_at": "2025-12-31T00:00:00Z" +} +``` + +Response includes the key value (only shown once): +```json +{ + "id": "uuid", + "name": "CI Pipeline Key", + "key": "aegis_a1b2c3d4e5f6...", + "scope": "read" +} +``` + +### Using a Key + +```http +Authorization: Bearer aegis_a1b2c3d4e5f6... +``` + +### Scopes + +| Scope | Allowed HTTP methods | +|-------|---------------------| +| `read` | GET, HEAD, OPTIONS only | +| `write` | GET, POST, PATCH, PUT, DELETE | +| `admin` | All + admin-only endpoints | + +A `read` scope key that attempts POST/PATCH/DELETE receives: +```json +{"detail": "API key scope 'read' does not permit this operation"} +``` +with status `403`. + +### Revoking a Key + +```http +DELETE /api/v1/api-keys/{id} +``` + +--- + +## SAML 2.0 SSO + +Aegis supports SAML 2.0 for enterprise single sign-on (e.g. Azure AD, Okta, Ping). + +### Configuration (admin only) + +```http +PUT /api/v1/sso/config +Content-Type: application/json + +{ + "enabled": true, + "idp_entity_id": "https://idp.example.com", + "idp_sso_url": "https://idp.example.com/sso/saml", + "idp_certificate": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----", + "sp_entity_id": "https://aegis.example.com", + "attribute_mapping": { + "email": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", + "role": "http://schemas.microsoft.com/ws/2008/06/identity/claims/groups" + } +} +``` + +### SSO Login Flow + +1. Browser visits `GET /api/v1/sso/login` → redirect to IdP +2. User authenticates with IdP +3. IdP POSTs SAML assertion to `POST /api/v1/sso/callback` +4. Aegis validates assertion, finds/creates user, issues JWT cookie +5. Browser redirected to frontend + +### Service Provider Metadata + +```http +GET /api/v1/sso/metadata +``` + +Returns XML metadata to register Aegis as a SAML service provider in your IdP. + +--- + +## Rate Limiting + +- **Login endpoint**: 5 attempts per minute per IP +- Exceeded: `429 Too Many Requests` +- Implemented via Redis counters with TTL + +--- + +## SSRF Protection (Webhooks) + +Webhook URLs are validated before saving. The following IP ranges are blocked: + +| Range | Description | +|-------|-------------| +| 10.0.0.0/8 | Private (Class A) | +| 172.16.0.0/12 | Private (Class B) | +| 192.168.0.0/16 | Private (Class C) | +| 169.254.0.0/16 | Link-local / APIPA | +| 127.0.0.0/8 | Loopback | +| ::1/128 | IPv6 loopback | +| fc00::/7 | IPv6 unique local | +| 0.0.0.0/8 | Reserved | +| 100.64.0.0/10 | Shared address space | + +Attempting to create a webhook pointing to a private IP returns: +```json +{"detail": "Webhook URL points to a private or reserved IP address (SSRF prevention)"} +``` + +--- + +## Audit Logging + +Every significant action is recorded in the `audit_logs` table (admin read-only): + +| Event type | Logged when | +|------------|-------------| +| `auth.login` | Successful login | +| `auth.logout` | Logout | +| `auth.login_failed` | Failed login attempt | +| `auth.password_changed` | Password change | +| `user.created` | New user created | +| `user.updated` | User updated | +| `user.deleted` | User deleted | +| `test.created` | New test created | +| `test.state_changed` | Test state transition | +| `test.validated` | Both leads approved | +| `campaign.completed` | Campaign completed | +| `permission.denied` | 403 on a protected endpoint | + +Query audit logs: +```http +GET /api/v1/audit-logs?user_id=&event_type=auth.login&from=2024-01-01&to=2024-12-31 +``` +""" + +PAGE_CAMPAIGNS = """\ +# Campaigns + +Campaigns group related tests under a common objective — for example, simulating a +specific threat actor, testing a specific tactic, or running a quarterly purple team exercise. + +--- + +## Campaign Types + +| Type | Description | +|------|-------------| +| `purple_team` | Collaborative red+blue exercise. Tests are executed with blue team full knowledge. | +| `red_team` | Blind offensive assessment. Blue team may not know which techniques will be tested. | +| `blue_team` | Detection-focused exercise. Blue team validates detection capabilities against known TTPs. | +| `tabletop` | Scenario walkthrough. No live execution — tests are planned and reviewed conceptually. | + +--- + +## Campaign Lifecycle + +``` +draft ──────────────> active ──────────────> completed + │ │ │ + │ POST /activate │ POST /complete │ + │ (leads, admin) │ (red_lead, admin) │ + │ │ │ + └── at least one └── execution └── final state + test required in progress (immutable) +``` + +### Draft + +- Campaign is being planned +- Tests can be added and removed +- Metadata (name, type, objective, schedule) can be edited + +**Create a campaign:** +```http +POST /api/v1/campaigns +Content-Type: application/json + +{ + "name": "Q1 2024 Purple Team — APT29 Simulation", + "description": "Full simulation of APT29 TTPs against corp environment", + "campaign_type": "purple_team", + "objective": "Measure detection coverage against Cozy Bear TTPs" +} +``` + +**Add tests:** +```http +POST /api/v1/campaigns/{id}/tests +{"test_id": "test-uuid"} +``` + +**Set schedule:** +```http +PATCH /api/v1/campaigns/{id}/schedule +{"start_date": "2024-03-01T09:00:00Z", "end_date": "2024-03-15T18:00:00Z"} +``` + +### Active + +- Triggered by POST /campaigns/{id}/activate (requires at least one linked test) +- Tests within the campaign are executed following the normal [Test-Lifecycle](Test-Lifecycle) +- Campaign-level progress is tracked in real time + +**Activate:** +```http +POST /api/v1/campaigns/{id}/activate +``` + +### Completed + +- Only red_lead or admin can complete a campaign +- All remaining tests are frozen in their current state +- Report generation becomes available for the full campaign + +**Complete:** +```http +POST /api/v1/campaigns/{id}/complete +{"completion_notes": "All priority techniques tested. 72% detection rate."} +``` + +--- + +## Creating a Campaign from a Threat Actor + +Aegis maintains a database of known threat actor profiles with associated MITRE techniques. +You can instantly scaffold a campaign for a threat actor: + +```http +POST /api/v1/campaigns/from-threat-actor/{actor_id} +Content-Type: application/json + +{ + "campaign_name": "APT29 Full Simulation", + "campaign_type": "purple_team", + "include_sub_techniques": true +} +``` + +This automatically: +1. Creates a new campaign +2. Creates one test draft per technique linked to the threat actor +3. Returns the campaign with all tests pre-populated + +--- + +## Progress Tracking + +```http +GET /api/v1/campaigns/{id}/progress +``` + +Returns: +```json +{ + "campaign_id": "uuid", + "total_tests": 24, + "validated": 18, + "in_progress": 4, + "not_started": 2, + "detection_rate": 0.72, + "progress_percent": 75.0 +} +``` + +--- + +## Campaign History + +Every state change in a campaign is logged: +```http +GET /api/v1/campaigns/{id}/history +``` + +Returns list of: +```json +{ + "timestamp": "2024-03-01T09:15:00Z", + "from_state": "draft", + "to_state": "active", + "actor": "red_lead_user", + "notes": null +} +``` + +--- + +## Reports for Campaigns + +Once a campaign is active or completed, a professional report can be generated: +```http +GET /api/v1/reports/generate/purple-campaign/{id}?format=pdf +``` + +See [Executive-Dashboard-and-Reports](Executive-Dashboard-and-Reports) for all report options. +""" + +PAGE_KNOWLEDGE = """\ +# Knowledge Management + +Aegis includes a knowledge management module for capturing institutional expertise: +**Playbooks** (procedural guides) and **Lessons Learned** (post-incident records). + +--- + +## Playbooks + +A playbook is a step-by-step procedural guide for a specific MITRE ATT&CK technique. +Each playbook is scoped to a `playbook_type` and a `technique_id`. + +### Playbook Types + +| Type | Target audience | Purpose | +|------|----------------|---------| +| `attack` | Red team | How to execute this technique | +| `defense` | Blue team | How to defend against this technique | +| `detection` | Blue team | How to detect this technique in logs/alerts | + +### Playbook Fields + +```json +{ + "title": "T1059.001 — PowerShell Attack Playbook", + "technique_id": "T1059.001", + "playbook_type": "attack", + "content": "# Overview\n\nThis playbook covers...\n\n## Prerequisites\n...", + "tools": ["Cobalt Strike", "PowerShell Empire", "Metasploit"], + "prerequisites": ["Domain user credentials", "Access to workstation"], + "is_active": true +} +``` + +### Versioning + +Every time a playbook is updated (PATCH), the system: +1. Creates a version snapshot with the previous content +2. Increments the version number +3. Records the author and timestamp + +**List versions:** +```http +GET /api/v1/knowledge/playbooks/{id}/versions +``` + +**Restore a previous version:** +```http +POST /api/v1/knowledge/playbooks/{id}/restore/{version_number} +``` + +This creates a new version from the restored content (non-destructive — the history +is always preserved). + +### Creating a Playbook + +```http +POST /api/v1/knowledge/playbooks +Content-Type: application/json + +{ + "title": "T1078 — Valid Account Detection Playbook", + "technique_id": "T1078", + "playbook_type": "detection", + "content": "## Indicators of Compromise\n\n1. Unusual login times...", + "tools": ["Splunk", "Elastic SIEM"], + "prerequisites": ["SIEM access", "AD event log forwarding configured"] +} +``` + +### Access Rules + +| Action | Required role | +|--------|--------------| +| Read any playbook | All roles (including viewer) | +| Create playbook | red_lead, blue_lead, admin | +| Update playbook | red_lead, blue_lead, admin | +| Delete playbook | red_lead, blue_lead, admin | +| Restore version | red_lead, blue_lead, admin | + +--- + +## Lessons Learned + +Lessons Learned records capture what happened during an exercise, the root cause, +and improvement actions. They can be linked to any entity in the system. + +### Lesson Fields + +```json +{ + "title": "AMSI bypass succeeded due to outdated signatures", + "what_happened": "Red team successfully bypassed AMSI on 3 of 5 endpoints...", + "root_cause": "AMSI signature database had not been updated in 45 days.", + "improvement": "Automate daily AMSI signature updates via WSUS. Set alert for stale updates.", + "severity": "high", + "tags": ["amsi", "evasion", "detection-gap"], + "technique_ids": ["T1562.001"], + "entity_type": "test", + "entity_id": "test-uuid" +} +``` + +### Severity Levels + +| Level | Description | +|-------|-------------| +| `low` | Minor finding, low risk | +| `medium` | Notable gap, moderate risk | +| `high` | Significant detection failure | +| `critical` | Systemic failure, immediate action required | + +### Entity Linking + +Lessons can be linked to any entity: + +| `entity_type` | Example `entity_id` | +|---------------|---------------------| +| `test` | UUID of the test | +| `campaign` | UUID of the campaign | +| `technique` | MITRE technique ID (T1059.001) | +| `detection_asset` | UUID of the detection asset | + +### Access Rules + +| Action | Required role | +|--------|--------------| +| Read any lesson | All roles (including viewer) | +| Create lesson | red_lead, blue_lead, admin | +| Update lesson | red_lead, blue_lead, admin | +| Delete lesson | red_lead, blue_lead, admin | + +--- + +## Knowledge Stats + +```http +GET /api/v1/knowledge/stats +``` + +Returns: +```json +{ + "playbooks": { + "total": 87, + "by_type": { + "attack": 32, + "defense": 28, + "detection": 27 + }, + "techniques_covered": 61 + }, + "lessons": { + "total": 43, + "by_severity": { + "critical": 3, + "high": 12, + "medium": 20, + "low": 8 + } + } +} +``` + +--- + +## Best Practices + +1. **Always link playbooks to tests**: When creating a test, reference the attack playbook in the test notes. +2. **Create lessons after every campaign**: Even successful tests yield lessons. Capture `what worked well`. +3. **Review playbooks quarterly**: Infrastructure changes may invalidate detection steps. +4. **Tag lessons**: Use consistent tags (`amsi`, `lateral-movement`, `cloud`) to make lessons searchable. +5. **One playbook per technique per type**: The system enforces uniqueness on `(technique_id, playbook_type)`. +""" + +PAGE_ALERTS = """\ +# Operational Alerts + +The operational alerts system monitors the state of your security coverage and +notifies the team when conditions fall below defined thresholds. + +--- + +## Alert Rules + +Alert rules define **what to check** and **when to fire**. Each rule has a type, +severity, configuration thresholds, and notification preferences. + +### Rule Types + +| Rule Type | What it checks | +|-----------|---------------| +| `coverage_drop` | Overall coverage score drops below a threshold | +| `stale_test` | A test has been in `red_executing` or `blue_evaluating` for too long | +| `unvalidated_test` | Tests stuck in `in_review` beyond a threshold duration | +| `high_risk_uncovered` | High-severity techniques have no validated tests | +| `detection_gap` | Technique has validated attack tests but no detection rule | + +### Rule Fields + +```json +{ + "name": "Coverage below 70%", + "description": "Alert when overall coverage drops below 70%", + "rule_type": "coverage_drop", + "severity": "high", + "config": { + "threshold": 70.0, + "tactic_id": null + }, + "is_enabled": true, + "cooldown_hours": 24, + "notify_in_app": true, + "notify_webhook": true, + "webhook_id": "webhook-uuid-or-null" +} +``` + +### Severity Levels + +| Severity | Use case | +|----------|---------| +| `info` | Informational; no action needed immediately | +| `low` | Worth noting but not urgent | +| `medium` | Should be addressed in next sprint | +| `high` | Requires prompt attention | +| `critical` | Immediate action required | + +### Rule Configuration Examples + +**Coverage drop:** +```json +{"threshold": 75.0} +``` +Fires when organization score drops below 75%. + +**Stale test:** +```json +{"stale_days": 7} +``` +Fires for any test in executing/evaluating state for more than 7 days. + +**High risk uncovered:** +```json +{"min_severity": "high", "max_uncovered": 5} +``` +Fires when more than 5 high-severity techniques have no validated test. + +**Detection gap:** +```json +{"require_detection_rule": true} +``` +Fires for every validated attack test that has no linked detection rule. + +--- + +## Alert Instances + +When a rule's condition is met and the rule is not in cooldown, an alert instance is created. + +### Instance Lifecycle + +``` +open ──────────────> acknowledged ──────────────> resolved + │ │ + └────────────────> dismissed │ + │ │ + └── suppressed until └── final state + cooldown resets (immutable) +``` + +### Instance Fields + +```json +{ + "id": "uuid", + "rule_id": "uuid", + "rule_name": "Coverage below 70%", + "rule_type": "coverage_drop", + "severity": "high", + "status": "open", + "details": {"current_score": 67.3, "threshold": 70.0}, + "fired_at": "2024-03-15T10:00:00Z", + "acknowledged_at": null, + "acknowledged_by": null, + "resolved_at": null, + "dismissed_at": null +} +``` + +--- + +## Alert Lifecycle Actions + +### Acknowledge + +Marks the alert as seen and being investigated. Does NOT suppress re-firing. +```http +POST /api/v1/alerts/{id}/acknowledge +{"notes": "Investigating coverage drop — two campaigns just completed"} +``` +Required role: red_lead, blue_lead, admin + +### Resolve + +Marks the underlying issue as fixed. Prevents re-evaluation from creating a +duplicate alert (until cooldown expires and condition is met again). +```http +POST /api/v1/alerts/{id}/resolve +{"resolution_notes": "Coverage restored to 78% after campaign validation"} +``` +Required role: red_lead, blue_lead, admin + +### Dismiss + +Suppresses the alert for the rule's cooldown period. +```http +POST /api/v1/alerts/{id}/dismiss +{"reason": "Planned maintenance window — coverage drop expected"} +``` +Required role: red_lead, blue_lead, admin + +--- + +## Alert Evaluation + +### Automatic (hourly) + +Aegis runs alert evaluation every hour via APScheduler: +- Checks all `is_enabled=true` rules +- For each rule, evaluates the condition against current data +- Creates an instance if condition is met AND rule is not in cooldown +- Sends in-app notifications and/or webhook calls per rule configuration + +### Manual trigger + +```http +POST /api/v1/alerts/evaluate +``` +Required role: red_lead, blue_lead, admin + +Useful when you've made changes and want to check immediately without waiting for the hourly job. + +--- + +## In-App Notifications + +When `notify_in_app: true` on a rule, an in-app notification is sent to all users +with role red_lead, blue_lead, or admin. + +View notifications: +```http +GET /api/v1/notifications +``` + +Mark as read: +```http +PATCH /api/v1/notifications/{id} +{"is_read": true} +``` + +--- + +## Webhook Notifications + +When `notify_webhook: true` and a `webhook_id` is set, Aegis POSTs to the configured +webhook URL when the alert fires. + +Webhook payload: +```json +{ + "event": "alert.fired", + "alert_id": "uuid", + "rule_name": "Coverage below 70%", + "severity": "high", + "details": {"current_score": 67.3, "threshold": 70.0}, + "fired_at": "2024-03-15T10:00:00Z" +} +``` + +--- + +## Summary + +```http +GET /api/v1/alerts/summary +``` + +Returns: +```json +{ + "total": 12, + "by_status": {"open": 5, "acknowledged": 3, "resolved": 3, "dismissed": 1}, + "by_severity": {"critical": 1, "high": 4, "medium": 5, "low": 2, "info": 0}, + "by_type": { + "coverage_drop": 2, + "stale_test": 4, + "unvalidated_test": 3, + "high_risk_uncovered": 2, + "detection_gap": 1 + } +} +``` +""" + +PAGE_DASHBOARD_REPORTS = """\ +# Executive Dashboard and Reports + +Aegis provides real-time dashboards, historical snapshots, and professional report +generation for all stakeholders — from technical leads to C-suite executives. + +--- + +## Dashboard Endpoints + +All dashboard endpoints require authentication but are accessible to all roles. + +### KPIs + +```http +GET /api/v1/dashboard/kpis +``` + +Returns the core metrics at a glance: +```json +{ + "total_techniques": 742, + "covered_techniques": 387, + "coverage_percent": 52.2, + "validated_tests": 312, + "active_campaigns": 3, + "open_alerts": 7, + "last_snapshot": "2024-03-15T08:00:00Z", + "organization_score": 64.8 +} +``` + +### Executive Summary + +```http +GET /api/v1/dashboard/executive +``` + +Returns a narrative summary suitable for non-technical stakeholders: +- Overall security posture statement +- Coverage trend (improving/declining/stable) +- Top 3 uncovered high-risk techniques +- Recent key achievements (campaigns completed, techniques covered) +- Open action items + +### Coverage by Tactic + +```http +GET /api/v1/dashboard/coverage-by-tactic +``` + +Returns per-tactic breakdown: +```json +[ + { + "tactic_id": "TA0002", + "tactic_name": "Execution", + "total_techniques": 13, + "validated": 8, + "partial": 3, + "not_covered": 2, + "coverage_percent": 84.6, + "score": 76.2 + }, + ... +] +``` + +### Posture History + +```http +GET /api/v1/dashboard/posture-history?days=90 +``` + +Time-series data for trend charts (default: last 90 days): +```json +[ + {"date": "2024-01-01", "score": 48.2, "covered_percent": 45.1}, + {"date": "2024-02-01", "score": 52.7, "covered_percent": 48.8}, + {"date": "2024-03-01", "score": 64.8, "covered_percent": 52.2} +] +``` + +### Activity Feed + +```http +GET /api/v1/dashboard/activity?limit=20 +``` + +Recent actions across the platform: +- Tests validated +- Campaigns completed +- New lessons learned added +- Alerts fired +- MITRE sync completed + +--- + +## Snapshots + +Snapshots capture a complete point-in-time record of coverage metrics. They enable +trend analysis and before/after comparison. + +### Creating a Snapshot + +**Manual** (leads, admin): +```http +POST /api/v1/snapshots +{"notes": "Pre-campaign baseline — March 2024"} +``` + +**Automatic**: The system creates snapshots automatically: +- After every campaign completion +- After MITRE sync +- On a scheduled basis (configurable) + +### Listing Snapshots + +```http +GET /api/v1/snapshots?limit=10&offset=0 +``` + +### Coverage Evolution (Trend) + +```http +GET /api/v1/snapshots/evolution?limit=50 +``` + +Returns time-ordered list of snapshots with key metrics — ideal for plotting a +trend line of coverage improvement over time. + +### Comparing Two Snapshots + +```http +GET /api/v1/snapshots/compare?a=&b= +``` + +Returns a diff showing: +- Techniques newly covered since snapshot A +- Techniques that lost coverage +- Score delta +- Test count delta + +### Deleting a Snapshot + +Only admins can delete snapshots: +```http +DELETE /api/v1/snapshots/{id} +``` + +--- + +## Professional Report Generation + +Aegis can generate publication-ready reports in PDF, DOCX, or HTML format. +These are available to: **admin, red_lead, blue_lead, and viewer**. + +### Available Reports + +| Report | Endpoint | Description | +|--------|----------|-------------| +| Purple Team Campaign | GET /reports/generate/purple-campaign/{id} | Full campaign report with all tests | +| Coverage Summary | GET /reports/generate/coverage-summary | Org-wide coverage status | +| Executive Summary | GET /reports/generate/executive-summary | C-suite briefing, 2-3 pages | +| Quarterly Summary | GET /reports/generate/quarterly-summary | Quarterly review with trends | +| Technique Detail | GET /reports/generate/technique/{id} | Deep-dive on one technique | + +### Format Selection + +Append `?format=pdf`, `?format=docx`, or `?format=html` (default: html): +```http +GET /api/v1/reports/generate/executive-summary?format=pdf +``` + +Response headers: +``` +Content-Type: application/pdf +Content-Disposition: attachment; filename="executive-summary-2024-03-15.pdf" +``` + +### Report Content — Purple Team Campaign + +Includes for each test in the campaign: +- Test title, technique ID and name, objective +- Execution timeline +- Red team findings (tool, command, output) +- Blue team detection result and response +- Validation status +- Evidence thumbnails +- Remediation status + +Plus campaign-level summary: +- Detection rate (detected / total tests) +- Coverage improvement delta +- Top findings and recommendations +- Executive narrative + +### Report Content — Executive Summary + +- Organization security posture score +- Coverage percentage vs last quarter +- Top 5 technique gaps by risk +- Recent campaign outcomes +- Key recommendations for next quarter +- Glossary of terms + +--- + +## Raw Data Exports + +| Endpoint | Format | Description | +|----------|--------|-------------| +| GET /reports/coverage-summary | JSON | Coverage status per technique | +| GET /reports/coverage-csv | CSV download | Coverage matrix for Excel/BI tools | +| GET /reports/test-results | JSON | All test results with outcomes | +| GET /reports/remediation-status | JSON | Remediation tracking per technique | +""" + +PAGE_DETECTION_LIFECYCLE = """\ +# Detection Lifecycle + +The Detection Lifecycle module provides a structured system for the Blue Team to manage, +validate, and track the health of detection capabilities mapped to MITRE ATT&CK techniques. + +--- + +## Overview + +The detection lifecycle tracks three main concepts: + +1. **Detection Assets** — What you use to detect (SIEM rules, EDR policies, sensors) +2. **Validations** — Proof that an asset correctly detected a technique +3. **Infrastructure Changes** — Events that may have broken your detections + +These three together provide a **confidence score** per technique: how certain are we +that our detections for technique T still work today? + +--- + +## Detection Assets + +A detection asset represents a defensive capability that can detect one or more techniques. + +### Asset Types + +| Type | Examples | +|------|---------| +| `siem_rule` | Splunk SPL query, KQL rule in Sentinel | +| `edr_policy` | CrowdStrike prevention policy, Defender ATP rule | +| `sensor` | Network sensor, honeypot, canary token | +| `ids_rule` | Snort/Suricata rule | +| `manual_review` | Human review process | +| `custom` | Any other detection mechanism | + +### Creating an Asset + +```http +POST /api/v1/detection-lifecycle/assets +Content-Type: application/json + +{ + "name": "Splunk: PowerShell AMSI Bypass Detection", + "asset_type": "siem_rule", + "description": "SPL search for AMSI bypass patterns in Windows PowerShell logs", + "owner_id": "blue-lead-uuid", + "team": "blue", + "platform": "Windows", + "rule_content": "index=wineventlog EventCode=4104 ScriptBlockText=*AmsiUtils*" +} +``` + +### Linking to Techniques + +```http +POST /api/v1/detection-lifecycle/assets/{id}/techniques/{technique_id} +``` + +One asset can be linked to multiple techniques. One technique can have multiple assets. + +--- + +## Validations + +A validation record proves that an asset successfully detected a technique at a +specific point in time. This is usually created after a successful test. + +### Creating a Validation + +```http +POST /api/v1/detection-lifecycle/validations +Content-Type: application/json + +{ + "asset_id": "asset-uuid", + "technique_id": "T1059.001", + "test_id": "test-uuid", + "validated_at": "2024-03-15T10:30:00Z", + "notes": "SIEM alert fired within 47 seconds of execution", + "confidence": "high" +} +``` + +### Invalidating a Validation + +When a detection rule changes, is disabled, or the infrastructure shifts, you should +invalidate the previous validation so the confidence score drops: + +```http +POST /api/v1/detection-lifecycle/validations/{id}/invalidate +{"reason": "SIEM rule rewritten — new version not yet tested"} +``` + +### Confidence Levels + +| Level | Meaning | +|-------|---------| +| `high` | Tested within 30 days, full detection | +| `medium` | Tested within 90 days OR partial detection | +| `low` | Tested more than 90 days ago | +| `unknown` | No validation exists | + +--- + +## Infrastructure Changes + +When infrastructure changes occur (SIEM migration, EDR update, log forwarding reconfigured), +existing validations may no longer be accurate. Recording an infrastructure change +automatically flags affected assets and adds techniques to the revalidation queue. + +### Recording a Change + +```http +POST /api/v1/detection-lifecycle/infrastructure-changes +Content-Type: application/json + +{ + "name": "Splunk → Microsoft Sentinel Migration", + "description": "All SIEM rules migrated from Splunk to KQL in Sentinel", + "change_type": "siem_migration", + "affected_asset_ids": ["asset-uuid-1", "asset-uuid-2"], + "occurred_at": "2024-03-01T00:00:00Z" +} +``` + +**Effect**: All validations linked to affected assets are automatically marked as +potentially stale. An alert rule of type `detection_gap` may fire. + +### Revalidation Queue + +After an infrastructure change, use the ownership module to see which techniques +need re-testing: +```http +GET /api/v1/ownership/revalidation-queue +``` + +Generate an updated queue: +```http +POST /api/v1/ownership/revalidation-queue/generate +``` + +--- + +## Detection Dashboard + +```http +GET /api/v1/detection-lifecycle/dashboard +``` + +Returns per-technique detection confidence: +```json +[ + { + "technique_id": "T1059.001", + "technique_name": "PowerShell", + "asset_count": 2, + "latest_validation": "2024-03-15T10:30:00Z", + "confidence": "high", + "days_since_validation": 5, + "pending_revalidation": false + }, + { + "technique_id": "T1078", + "technique_name": "Valid Accounts", + "asset_count": 1, + "latest_validation": "2023-11-10T08:00:00Z", + "confidence": "low", + "days_since_validation": 125, + "pending_revalidation": true + } +] +``` + +--- + +## Detection Rules + +Detection rules are the concrete SIEM/EDR rule definitions linked to Aegis tests. +They complement detection assets by providing the exact rule logic. + +### Creating a Rule + +```http +POST /api/v1/detection-rules +Content-Type: application/json + +{ + "name": "PowerShell Encoded Command Execution", + "technique_ids": ["T1059.001"], + "platform": "Windows", + "rule_type": "siem", + "rule_content": "EventCode=4104 ScriptBlockText=*-EncodedCommand*", + "description": "Detects PowerShell execution with base64 encoded commands", + "severity": "high", + "is_enabled": true +} +``` + +### Evaluating a Rule Against a Test + +After running a test, record whether the rule fired: +```http +POST /api/v1/detection-rules/evaluate +{ + "rule_id": "rule-uuid", + "test_id": "test-uuid", + "result": "triggered", + "time_to_detect_seconds": 23, + "notes": "Rule fired on first PowerShell invocation" +} +``` + +`result` options: `triggered`, `not_triggered`, `false_positive` +""" + +PAGE_DEPLOYMENT = """\ +# Deployment Guide + +This guide covers deploying Aegis in both development and production environments. + +--- + +## Prerequisites + +| Requirement | Version | Notes | +|-------------|---------|-------| +| Docker | 24+ | Required | +| Docker Compose | v2 (plugin) | `docker compose` (not `docker-compose`) | +| Git | Any | For pulling updates | +| 4GB RAM | Minimum | 8GB recommended for production | +| 20GB disk | Minimum | For database + evidence files | + +--- + +## Development Setup + +### 1. Clone and configure + +```bash +git clone https://git.undiamagico.es/kitos/Aegis +cd Aegis +cp .env.example .env +# Edit .env with your settings +``` + +### 2. Start services + +```bash +docker compose up -d +``` + +This starts all services: +- Backend API: http://localhost:8000 +- Frontend: http://localhost:5173 +- Swagger UI: http://localhost:8000/docs +- ReDoc: http://localhost:8000/redoc +- MinIO Console: http://localhost:9001 +- PostgreSQL: localhost:5433 + +### 3. Verify + +```bash +curl http://localhost:8000/health +# {"status": "ok"} +``` + +### 4. First login + +Default admin credentials are set via `ADMIN_USERNAME` and `ADMIN_PASSWORD` in `.env`. + +```bash +curl -X POST http://localhost:8000/api/v1/auth/login \\ + -d "username=admin&password=yourpassword" \\ + -c cookies.txt +``` + +--- + +## Production Deployment + +### 1. Prepare server + +```bash +# On the server +mkdir -p /opt/aegis +cd /opt/aegis +git clone https://git.undiamagico.es/kitos/Aegis . +cp .env.example .env +# Edit .env — see Required Environment Variables section +``` + +### 2. Deploy + +```bash +docker compose -f docker-compose.prod.yml up -d --build +``` + +### 3. Verify + +```bash +docker compose -f docker-compose.prod.yml ps +# All services should show "healthy" or "running" + +curl http://localhost:8000/health +# {"status": "ok"} +``` + +### 4. Update + +```bash +git pull +docker compose -f docker-compose.prod.yml up -d --build +``` + +--- + +## Required Environment Variables + +Create a `.env` file at the project root: + +```env +# ─── Database ───────────────────────────────────────────── +DATABASE_URL=postgresql+asyncpg://aegis:strongpassword@aegis-postgres:5432/aegis + +# ─── Security ───────────────────────────────────────────── +SECRET_KEY=your-256-bit-random-secret-key-here +ACCESS_TOKEN_EXPIRE_MINUTES=480 + +# ─── Application ───────────────────────────────────────── +AEGIS_ENV=production # or "development" +SECURE_COOKIES=auto # auto | true | false + +# ─── First-run Admin ────────────────────────────────────── +ADMIN_USERNAME=admin +ADMIN_PASSWORD=ChangeMe123! # Must meet password policy + +# ─── MinIO (Object Storage) ─────────────────────────────── +MINIO_ENDPOINT=aegis-minio:9000 +MINIO_ACCESS_KEY=minioadmin +MINIO_SECRET_KEY=minio-secret-key +MINIO_BUCKET=aegis-evidence +MINIO_USE_SSL=false # true if MinIO behind HTTPS + +# ─── Redis ──────────────────────────────────────────────── +REDIS_URL=redis://aegis-redis:6379/0 + +# ─── Optional: Jira Integration ─────────────────────────── +# JIRA_URL=https://yourorg.atlassian.net +# JIRA_USERNAME=service-account@yourorg.com +# JIRA_API_TOKEN=your-jira-api-token +# JIRA_PROJECT_KEY=SEC + +# ─── Optional: CORS ─────────────────────────────────────── +# CORS_ORIGINS=https://aegis.yourcompany.com +``` + +### Generating SECRET_KEY + +```bash +python3 -c "import secrets; print(secrets.token_hex(32))" +``` + +--- + +## Database Migrations + +Migrations are managed with **Alembic** and run automatically on container start +in production via `entrypoint.prod.sh`: + +```bash +#!/bin/bash +# entrypoint.prod.sh +alembic upgrade head +uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 4 +``` + +**Manual migration** (if needed): +```bash +docker compose exec aegis-backend alembic upgrade head +``` + +**Create a new migration** (development): +```bash +docker compose exec aegis-backend alembic revision --autogenerate -m "add_new_field" +``` + +--- + +## Port Reference + +| Service | Development port | Production notes | +|---------|-----------------|------------------| +| Backend API | 8000 | Typically proxied via nginx/Traefik | +| Frontend | 5173 | Served as static files via nginx in prod | +| PostgreSQL | 5433 (host) | Not exposed in production | +| MinIO API | 9000 | Not exposed in production | +| MinIO Console | 9001 | Optional admin access | +| Redis | 6379 | Not exposed in production | + +--- + +## HTTPS / Reverse Proxy + +In production, place a reverse proxy (nginx, Traefik, Caddy) in front of Aegis: + +**nginx example:** +```nginx +server { + listen 443 ssl; + server_name aegis.yourcompany.com; + + ssl_certificate /etc/ssl/certs/aegis.crt; + ssl_certificate_key /etc/ssl/private/aegis.key; + + location /api/ { + proxy_pass http://localhost:8000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto https; + } + + location / { + proxy_pass http://localhost:5173; + proxy_set_header Host $host; + } +} +``` + +--- + +## Running Tests + +```bash +# Full test suite +cd backend && pytest tests/ -v + +# Specific module +pytest tests/test_auth.py -v + +# With coverage report +pytest tests/ --cov=app --cov-report=html + +# Fast (no DB — unit tests only) +pytest tests/ -v -m "not integration" +``` + +The test suite has **367+ tests** covering all major modules. + +--- + +## Linting + +```bash +# Check +ruff check . + +# Fix auto-fixable issues +ruff check . --fix + +# Format +ruff format . +``` + +--- + +## Logs + +```bash +# Follow backend logs +docker compose logs -f aegis-backend + +# All services +docker compose logs -f + +# Last 100 lines +docker compose logs --tail=100 aegis-backend +``` + +--- + +## Backup + +### Database + +```bash +# Dump +docker compose exec aegis-postgres pg_dump -U aegis aegis > backup_$(date +%Y%m%d).sql + +# Restore +docker compose exec -T aegis-postgres psql -U aegis aegis < backup_20240315.sql +``` + +### Evidence files (MinIO) + +```bash +# Using mc (MinIO client) +mc mirror myminio/aegis-evidence ./evidence-backup/ +``` + +--- + +## Troubleshooting + +| Issue | Solution | +|-------|---------| +| Backend fails to start | Check `SECRET_KEY` is set in production | +| 401 on all requests | Check `SECURE_COOKIES` — may be forcing HTTPS on HTTP connection | +| MinIO errors | Verify `MINIO_BUCKET` exists; check credentials | +| Migration fails | Check `DATABASE_URL` is correct and DB is reachable | +| Redis connection refused | Verify `REDIS_URL` and that redis container is running | +| Swagger not loading | Expected in production (`AEGIS_ENV=production` disables it) | +""" + +PAGE_QA_GUIDE = """\ +# QA Testing Guide + +This page documents the automated QA runner and manual testing checklist for Aegis. +The QA suite validates access control, test lifecycle, API key scoping, and security +controls. + +--- + +## Automated QA Runner + +The automated QA runner is located at `scripts/qa_runner.py`. + +### What it Tests + +The runner executes **77 automated tests** organized as follows: + +1. **Setup** — Creates test users for all 6 roles (admin, red_lead, blue_lead, + red_tech, blue_tech, viewer). Creates a test technique. + +2. **Authentication tests** + - Login for each role + - Verify `/auth/me` returns correct role + - Verify `must_change_password` blocks all endpoints + - Verify logout revokes token (subsequent request returns 401) + +3. **Full test lifecycle** (happy path) + - Create test as red_lead → draft + - Start execution as red_tech → red_executing + - Verify blue_tech cannot submit-red (403) + - Submit red as red_tech → blue_evaluating + - Verify red_tech cannot update blue fields (403) + - Update blue fields as blue_tech + - Submit blue as blue_tech → in_review + - Validate red as red_lead → red approved + - Validate blue as blue_lead → both approved → validated + - Verify technique coverage updated + +4. **Access control denials (403 checks)** + For each protected endpoint, verify forbidden roles receive 403: + - viewer cannot POST /tests + - viewer cannot POST /tests/{id}/start-execution + - blue_tech cannot PATCH /tests/{id}/red + - red_tech cannot PATCH /tests/{id}/blue + - blue_lead cannot POST /tests/{id}/complete (campaign) + - red_tech cannot GET /audit-logs + - non-admin cannot POST /webhooks + - non-admin cannot DELETE /snapshots/{id} + - non-admin cannot POST /system/sync-mitre + +5. **API Key scope enforcement** + - Create read-scope key as red_lead + - Verify GET /tests returns 200 with read key + - Verify POST /tests returns 403 with read key + - Verify PATCH /tests/{id} returns 403 with read key + - Create write-scope key + - Verify POST /tests returns 201 with write key + +6. **SSRF webhook protection** + - Attempt to create webhook pointing to 192.168.1.1 → expect 400 (SSRF blocked) + - Attempt to create webhook pointing to 127.0.0.1 → expect 400 + - Attempt to create webhook pointing to 10.0.0.1 → expect 400 + - Attempt with valid public URL → expect 201 + +7. **Campaign lifecycle** + - Create campaign as red_lead + - Add tests to campaign + - Activate campaign + - Verify viewer can read campaign progress + - Verify blue_lead cannot complete campaign (403) + - Complete campaign as red_lead + +8. **Cleanup** + - Delete all created tests + - Delete all created campaigns + - Delete all created users + - Verify cleanup success + +### Running the QA Suite + +```bash +# Against local dev +python scripts/qa_runner.py --base-url http://localhost:8000 + +# Against production (use with caution) +python scripts/qa_runner.py --base-url https://aegis.yourcompany.com + +# Verbose mode (shows each HTTP request) +python scripts/qa_runner.py --base-url http://localhost:8000 --verbose + +# Run specific phase only +python scripts/qa_runner.py --base-url http://localhost:8000 --phase lifecycle +``` + +### Exit Codes + +| Code | Meaning | +|------|---------| +| 0 | All tests passed | +| 1 | One or more tests failed | +| 2 | Setup failed (cannot run further tests) | + +### Sample Output + +``` +Creating Aegis QA Test Run... +[SETUP] Creating test users... + [OK] Created admin user: qa_admin_20240315 + [OK] Created red_lead user: qa_rl_20240315 + [OK] Created blue_lead user: qa_bl_20240315 + [OK] Created red_tech user: qa_rt_20240315 + [OK] Created blue_tech user: qa_bt_20240315 + [OK] Created viewer user: qa_v_20240315 + +[PHASE 1] Authentication... + [OK] Login as admin + [OK] Login as red_lead + ... + +[PHASE 2] Test Lifecycle... + [OK] Create test (red_lead) + [OK] Start execution (red_tech) + [FAIL] 403 for viewer trying start-execution — expected 403, got 403 ✓ + ... + +Results: 77/77 passed +``` + +--- + +## Manual QA Checklist + +### admin role + +- [ ] Login succeeds +- [ ] Change password on first login +- [ ] Can list all users (`GET /users`) +- [ ] Can create a new user (POST /users) +- [ ] Can delete a user (DELETE /users/{id}) +- [ ] Can access audit logs (`GET /audit-logs`) +- [ ] Can trigger MITRE sync (POST /system/sync-mitre) +- [ ] Can create a webhook (POST /webhooks) +- [ ] Can view scheduler status (`GET /system/scheduler-status`) +- [ ] Can delete a snapshot (DELETE /snapshots/{id}) +- [ ] Can set data classification on a test (PATCH /tests/{id}/classification) +- [ ] Can complete a campaign (POST /campaigns/{id}/complete) +- [ ] Can validate both red and blue sides of a test + +### red_lead role + +- [ ] Login succeeds +- [ ] Can create a test (POST /tests) +- [ ] Can start test execution +- [ ] Can validate red side of test in `in_review` +- [ ] Can create a campaign and add tests +- [ ] Can complete a campaign +- [ ] Can create a playbook +- [ ] Can create a lesson learned +- [ ] Cannot access audit logs → 403 +- [ ] Cannot access webhooks → 403 +- [ ] Cannot validate blue side → 403 +- [ ] Cannot delete snapshots → 403 +- [ ] Can generate a PDF report + +### blue_lead role + +- [ ] Login succeeds +- [ ] Can validate blue side of test in `in_review` +- [ ] Can create detection assets and link to techniques +- [ ] Can create detection validations +- [ ] Can generate revalidation queue +- [ ] Cannot start test execution → 403 +- [ ] Cannot complete a campaign → 403 +- [ ] Cannot validate red side → 403 +- [ ] Cannot access webhooks → 403 +- [ ] Can generate a PDF report + +### red_tech role + +- [ ] Login succeeds +- [ ] Can start test execution (test must be in draft) +- [ ] Can update red fields (PATCH /tests/{id}/red) in red_executing state +- [ ] Can upload red evidence +- [ ] Can submit red (POST /tests/{id}/submit-red) +- [ ] Cannot create a test → 403 +- [ ] Cannot update blue fields → 403 +- [ ] Cannot validate either side → 403 +- [ ] Cannot generate reports → 403 +- [ ] Can pause/resume timer + +### blue_tech role + +- [ ] Login succeeds +- [ ] Can update blue fields (PATCH /tests/{id}/blue) in blue_evaluating state +- [ ] Can upload blue evidence (only in blue_evaluating state) +- [ ] Can submit blue (POST /tests/{id}/submit-blue) +- [ ] Can evaluate detection rules (POST /detection-rules/evaluate) +- [ ] Cannot start execution → 403 +- [ ] Cannot submit red → 403 +- [ ] Cannot validate either side → 403 +- [ ] Cannot create tests → 403 +- [ ] Can pause/resume timer + +### viewer role + +- [ ] Login succeeds +- [ ] Can read tests, campaigns, techniques, coverage, heatmap, dashboard +- [ ] Can generate PDF/DOCX/HTML reports +- [ ] Cannot create a test → 403 +- [ ] Cannot start execution → 403 +- [ ] Cannot update any test fields → 403 +- [ ] Cannot upload evidence → 403 +- [ ] Cannot validate → 403 +- [ ] Cannot create campaigns → 403 +- [ ] Cannot create playbooks or lessons → 403 +- [ ] Cannot pause/resume timer → 403 + +--- + +## Security QA Checklist + +### SSRF Protection + +- [ ] POST /webhooks with URL `http://192.168.1.1/evil` → 400 Bad Request +- [ ] POST /webhooks with URL `http://10.0.0.1/evil` → 400 Bad Request +- [ ] POST /webhooks with URL `http://127.0.0.1/evil` → 400 Bad Request +- [ ] POST /webhooks with URL `http://169.254.169.254/latest/meta-data` → 400 +- [ ] POST /webhooks with URL `https://hooks.slack.com/services/valid` → 201 OK + +### API Key Scopes + +- [ ] Read-scope key: GET requests succeed (200) +- [ ] Read-scope key: POST requests fail (403) +- [ ] Read-scope key: PATCH requests fail (403) +- [ ] Read-scope key: DELETE requests fail (403) +- [ ] Write-scope key: POST requests succeed (201) +- [ ] Admin-scope key: admin-only endpoints succeed + +### Token Blacklisting + +- [ ] Logout request succeeds +- [ ] Using old token after logout returns 401 +- [ ] New login after logout works correctly + +### Rate Limiting + +- [ ] 6 rapid failed login attempts → 429 Too Many Requests on 6th + > Note: This test cannot be run from loopback (127.0.0.1) if the rate limiter + > is configured to skip localhost. Run from an external IP in production testing. + +--- + +## Known Test Limitations + +| Limitation | Reason | Workaround | +|------------|--------|------------| +| Rate limit not testable from loopback | Redis rate limiter may skip 127.0.0.1 | Test from external network | +| SSRF blocks may not apply to all DNS names | DNS resolves at request time, not save time | Use IP addresses in SSRF tests | +| PDF generation requires wkhtmltopdf/weasyprint | Must be installed in container | Check container logs if PDF tests fail | +| SAML SSO testing requires an IdP | Real IdP needed for full SSO test | Use mock IdP (e.g. samltest.id) | +| Evidence download requires MinIO | MinIO must be running and bucket configured | Use dev docker-compose | +| Cleanup may fail if tests are in validated state | Validated tests may be immutable | Admin override required for cleanup | + +--- + +## Phase-by-Phase QA Scripts + +In addition to `qa_runner.py`, the `scripts/` directory contains phase-specific +scripts for testing individual feature areas: + +| Script | Phase covered | +|--------|--------------| +| `qa_phases_6_7.py` | Attack paths, knowledge management | +| `qa_phase8.py` | Ownership, risk, analytics | +| `qa_phase9.py` | Alerts, detection lifecycle | +| `qa_phase10.py` | Reports, snapshots, heatmap | +| `qa_phase11.py` | Threat actors, compliance | +| `qa_phase12.py` | SSO, API keys, webhooks | +| `qa_phase13.py` | Operational alerts (Phase 13 gaps) | +| `qa_phase14.py` | Final integration tests | +| `qa_full.py` | Full end-to-end suite | + +Run any of these directly: +```bash +python scripts/qa_phase13.py --base-url http://localhost:8000 +``` +""" + +# ───────────────────────────────────────────── +# PAGES DICT +# ───────────────────────────────────────────── + +PAGES = { + "Home": PAGE_HOME, + "Architecture": PAGE_ARCHITECTURE, + "Roles-and-Permissions": PAGE_ROLES, + "Test-Lifecycle": PAGE_TEST_LIFECYCLE, + "MITRE-ATT-CK-Coverage": PAGE_MITRE, + "API-Reference": PAGE_API_REF, + "Authentication-and-Security": PAGE_AUTH_SECURITY, + "Campaigns": PAGE_CAMPAIGNS, + "Knowledge-Management": PAGE_KNOWLEDGE, + "Operational-Alerts": PAGE_ALERTS, + "Executive-Dashboard-and-Reports": PAGE_DASHBOARD_REPORTS, + "Detection-Lifecycle": PAGE_DETECTION_LIFECYCLE, + "Deployment-Guide": PAGE_DEPLOYMENT, + "QA-Testing-Guide": PAGE_QA_GUIDE, +} + + +def main(): + print("Creating Aegis Wiki...") + print(f"Target: {GITEA_URL}/api/v1/repos/{OWNER}/{REPO}/wiki/pages") + print(f"Pages to create: {len(PAGES)}\n") + + results = {} + for title, content in PAGES.items(): + line_count = content.count("\n") + print(f" Processing: {title} (~{line_count} lines)") + ok = create_or_update_page(title, content) + results[title] = ok + time.sleep(0.5) # Be gentle with the API + + passed = sum(results.values()) + total = len(results) + print(f"\nDone: {passed}/{total} pages created successfully") + + if passed < total: + print("\nFailed pages:") + for t, ok in results.items(): + if not ok: + print(f" FAILED: {t}") + sys.exit(1) + else: + print("\nAll wiki pages created successfully!") + print(f"View at: {GITEA_URL}/{OWNER}/{REPO}/wiki") + + +if __name__ == "__main__": + main()