Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Creates 14 comprehensive wiki pages covering architecture, roles, test lifecycle, API reference, security, deployment, and QA guide. Run from a machine with access to internal Gitea (192.168.1.107:3000).
3339 lines
104 KiB
Python
3339 lines
104 KiB
Python
#!/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=<from_env>
|
||
```
|
||
|
||
The server sets an `aegis_token` HttpOnly cookie. All subsequent requests are
|
||
authenticated automatically by the browser (or pass the token as
|
||
`Authorization: Bearer <token>`).
|
||
|
||
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": "<old>", "new_password": "<new_12chars+>"}
|
||
```
|
||
|
||
### 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 <token>` 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=<binary>
|
||
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=<binary>
|
||
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 <token>`. 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=<id>&b=<id> |
|
||
|
||
---
|
||
|
||
## 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_<hex>`.
|
||
|
||
---
|
||
|
||
## 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=<JWT>; 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 <JWT>
|
||
```
|
||
|
||
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: <seconds until token expiry>
|
||
```
|
||
|
||
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=<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=<snapshot_id_1>&b=<snapshot_id_2>
|
||
```
|
||
|
||
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()
|