SecureLab
SecureLab is the attack simulation and detection validation platform in the opensecstack (SIN) ecosystem. It answers the question that detection stacks cannot answer themselves: do your deployed defences actually fire when the technique is executed? Scenarios are mapped to MITRE ATT&CK, run inside isolated Docker networks, and every run emits immutable evidence to CITADEL.
docs/safety-controls.md before executing any scenario.Overview
Detection rules degrade silently. Log pipelines change, field names are renamed, and a rule that matched in 2025 can fail with zero alert in 2027 without anyone noticing. SecureLab closes this gap with three complementary capabilities:
- Continuous detection validation. After each scenario run the detection validator polls OpenScrub, APIGuard, and ThreatFlow for expected alert events within a configurable window, returning a per-step
detected / not_detected / inconclusiveverdict. - ATT&CK coverage measurement. The MITRE ATT&CK mapper builds a coverage matrix from executed scenarios — coverage is measured by execution outcome, not by rule existence.
- Immutable audit trail. Every live run emits a
securelab.run_completedevent (HMAC-SHA256 signed) to CITADEL, providing an externally verifiable record of all simulation activity including timed-out and blocked runs.
SecureLab is licensed under Apache 2.0. Offensive tooling embedded in the platform does not grant authorisation to test systems you do not own or have explicit written permission to test.
Key features
| Module | Description |
|---|---|
| Scenario Engine | Author, version, and execute multi-step YAML attack scenarios; content-hashed on write so every execution references an immutable scenario version |
| Attack Library | 15 built-in attack primitives tagged with MITRE ATT&CK technique IDs, tactics, and target platforms; extensible via YAML |
| MITRE ATT&CK Mapper | Maps scenarios to techniques and sub-techniques; generates a coverage heatmap and gap analysis from execution history |
| Detection Validator | Polls OpenScrub (/api/v1/alerts), APIGuard (/api/v1/anomalies), and ThreatFlow (/api/v1/ioc-matches) read-only over HMAC-signed requests; returns per-step verdicts within a configurable detection window |
| Payload Fuzzer | Rust payload-generation crate (called via PyO3 / subprocess) generating encoding variants, byte mutations, and fuzzing campaigns to stress-test detection rule boundaries |
| CITADEL Evidence Emitter | Async securelab.run_completed events to CITADEL WORM; dry-run executions do not emit; circuit breaker marks runs evidence_pending if emission fails |
| IRFlow Integration | Pushes execution results and ATT&CK coverage gaps to IRFlow via POST /api/v1/securelab/results for incident-response correlation |
| Safety controls | Target URL blocklist, --internal Docker networks, per-attack-type rate caps, hard 30-minute scenario timeouts, admin-only environment creation, append-only audit log (Postgres row-level security), and pre-execution audit record creation — all non-bypassable at the platform level |
Architecture
The v1.0.0 backend is written in Go (chi router, pgx, zap) and runs on :8080. The scenario engine and detection monitor run inside the same process; async scenario execution uses background goroutines with Redis-backed task state. The Rust payload-gen crate is compiled as a standalone library and invoked from Go via subprocess or shared-library binding.
Test target environments are provisioned on Docker bridge networks with internal: true — target containers have no outbound internet access and cannot reach any host outside the isolated test network. This cannot be overridden via the SecureLab API.
The platform enforces a strict security boundary: the API port (8080) and dashboard port (3000) must not be reachable from the public internet. Outbound from the Celery worker or detection monitor to integration endpoints must be allow-listed by IP and port at the firewall level.
The audit_log table is INSERT-only at the database level (Postgres row-level security with USING (false) on UPDATE/DELETE for the API service role). Every scenario run is recorded before execution begins, so blocked and timed-out runs also have a complete audit trail.
executions.evidence_hash stores a BLAKE3 hash of the canonical evidence body submitted to CITADEL, enabling independent audit verification.
Ports & endpoints
| Port | Service |
|---|---|
8080 | SecureLab REST API (Go — v1.0.0) |
3000 | React operator dashboard (nginx in production) |
8087 | SecureLab REST API (Python/FastAPI — scaffold reference) |
3007 | React operator dashboard (scaffold reference) |
| Method | Path | Purpose |
|---|---|---|
GET | /api/v1/health | Liveness + DB ping |
GET | /api/v1/scenarios | List scenarios with ATT&CK mapping |
POST | /api/v1/scenarios | Create scenario (YAML body, content-hashed on write) |
POST | /api/v1/scenarios/{id}/execute | Execute scenario — dry_run: true for plan-only |
GET | /api/v1/executions/{exec_id} | Execution status and result |
GET | /api/v1/executions/{exec_id}/detections | Per-step detection verdicts |
GET | /api/v1/coverage | ATT&CK technique coverage matrix |
GET | /api/v1/coverage/{technique_id} | Coverage detail for one technique |
GET | /api/v1/attack-library | List all attack primitives |
Integration
SecureLab authenticates dashboard users via sinauth SSO using the OAuth 2.0 authorization_code + PKCE (S256) flow. The API validates RS256-signed tokens against the sinauth JWKS endpoint at https://auth.sin.to/.well-known/jwks.json.
Minimum required environment variables:
SECURELAB_DB_URL=postgres://securelab:securelab@postgres:5432/securelab SECURELAB_REDIS_URL=redis://redis:6379/0 SECURELAB_CITADEL_API_URL=https://citadel.internal SECURELAB_CITADEL_KEY_SECRET=<hmac secret> SECURELAB_OPENSCRUB_API_URL=https://openscrub.internal SECURELAB_APIGUARD_API_URL=https://apiguard.internal SECURELAB_THREATFLOW_API_URL=https://threatflow.internal SECURELAB_IRFLOW_API_URL=https://irflow.internal SECURELAB_ISOLATION_MODE=strict SECURELAB_CITADEL_DRY_RUN=false
Quick start with Docker Compose:
git clone https://github.com/opensecstack/opensecstack cd opensecstack/securelab cp .env.example .env docker compose up -d "cm"># Health check curl http://localhost:8080/api/v1/health "cm"># List available scenarios curl -H "Authorization: Bearer $TOKEN" \ http://localhost:8080/api/v1/scenarios "cm"># Dry-run a scenario (no payloads sent — plan only) curl -X POST http://localhost:8080/api/v1/scenarios/api-bola-basic/execute \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"dry_run": true}'
A YAML scenario maps each step to an attack primitive from the library with MITRE ATT&CK technique IDs:
name: bola-basic description: "BOLA via sequential integer ID enumeration on REST objects" mitre_technique_ids: ["T1078"] tags: [api, owasp-a1, bola] severity: high timeout: 3m steps: - kind: bola params: endpoint: /api/v1/users/{id} id_range: [1, 100] auth_token_param: low_privilege_jwt
Full documentation
The complete SecureLab reference — scenario specification, attack library, detection validation guide, safety controls, MITRE ATT&CK mapping, CITADEL integration, and operator handbook — is in the repository:
github.com/opensecstack/opensecstack/tree/main/securelab/docs