Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion .claude/skills/implement/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: implement
description: Assess and implement a Ground Control requirement, ensuring GitHub issue and traceability links exist
description: Assess and implement a requirement stored in Ground Control, ensuring GitHub issue and traceability links exist
argument-hint: <requirement-uid>
disable-model-invocation: true
---
Expand Down Expand Up @@ -38,8 +38,27 @@ Explore the codebase to determine whether the requirement described in the issue
- **If the requirement is NOT yet met**: Plan the implementation. Identify which files need to be created or modified, what tests to write, and what approach to take. Enter plan mode.
- Your plans must respect the coding standards and classification levels in ADR-012.
- Plans must include updating the changelog, readme, and docs as appropriate.
- If designing code, remember to build off existing cross-cutting concerns, code, and patterns
- Good code is readable, maintainable, and follows the coding standards
- Address the concerns a FAANG L6+ engineer would have around security, performance, reliability, and scalability
- Avoid reinventing the wheel - use existing libraries and frameworks where appropriate
- Code should be easy to understand, test, and maintain. Simple is better than complex.
- **If the requirement IS already met**: Report that the requirement is satisfied and identify which code satisfies it.

## Step 4.5: Clause-by-Clause Verification

Before declaring implementation complete:
1. Re-read the requirement statement from Step 1.
2. Break it into individual clauses and acceptance criteria.
3. For EACH clause, identify the specific code (file:line) that satisfies it.
4. If any clause is not satisfied, go back and implement it before proceeding.

Present the mapping as a checklist:
- [ ] Clause: "..." → Satisfied by: file.java:line
- [ ] Clause: "..." → Satisfied by: file.java:line

Do not proceed to Step 5 until every clause is checked off.

## Step 5: Ensure Traceability Links

After implementation is complete (or if already implemented):
Expand All @@ -50,3 +69,15 @@ After implementation is complete (or if already implemented):
- use the `gc_transition_status` MCP tool to transition the requirement to `ACTIVE` if it was `DRAFT`.

Do not update the Changelog if all you did was operate Ground Control tools.

## Step 6: Completion Gate

Implementation is NOT complete until ALL of the following are verified:

1. **`make check` passes** — run it and confirm BUILD SUCCESSFUL.
2. **CHANGELOG.md updated** — verify it is in `git diff --name-only` if any source files changed.
3. **Traceability links exist** — re-fetch with `gc_get_traceability` and confirm IMPLEMENTS and TESTS links are present.
4. **Requirement status is ACTIVE** — re-fetch with `gc_get_requirement` and confirm status.
5. **Step 4.5 clause mapping was completed** — if you skipped it, go back and do it now.

If any check fails, fix it before reporting completion. Do NOT report the implementation as done until every check passes.
112 changes: 112 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,118 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [6.0.1] - 2026-03-29

### Fixed

- SDL parser now catches `pydantic.ValidationError` instead of bare `Exception`, so implementation bugs propagate as real errors instead of masquerading as parse failures
- Removed dead `VM` and `Switch` classes from `nodes.py` — the unified `Node` class with type discrimination replaced them
- Validator node-name-length check now uses the `MAX_NODE_NAME_LENGTH` constant instead of a hardcoded `35`
- Replaced `list.pop(0)` with `deque.popleft()` in topological sort for O(1) per operation
- Removed redundant `_all_entity_names()` update since `flatten_entities` already includes top-level keys
- Fixed overly permissive assertion in `test_hyphenated_keys` — now pins the expected preserved-key behavior
- Moved `TestPlatformCommand` from `test_sdl_models.py` to `tests/test_attacks.py` since `PlatformCommand` lives in `aptl.core.attacks`, not the SDL package
- Corrected ADR-014 file count from 24 to 21 (reflecting compat.py/defenses.py/attacks.py removal in 6.0.0)

## [6.0.0] - 2026-03-29

### Changed

- **BREAKING**: Removed APTL legacy fields from SDL Scenario model — `metadata`, `mode`, `containers`, `preconditions`, `scoring`, `attack_chain`, `steps`, and `defenses` are no longer part of the SDL specification. The legacy `objectives` block was not kept verbatim; it was replaced by declarative SDL `objectives`, and the SDL now also includes first-class `workflows` for branching/parallel objective control. The SDL now has one clean identity: 21 specification sections with `name` as the only required field.
- **BREAKING**: Removed backward-compatibility layer (`sdl/compat.py`) and the `scenarios.py` re-export shim. Runtime code that imported `ScenarioDefinition`, `validate_scenario_containers`, or APTL-specific types from `aptl.core.scenarios` will need updating.
- **BREAKING**: APTL legacy scenario YAMLs (with `metadata` block) no longer parse through the SDL. They require migration to SDL format.
- Tightened structural validation for the new SDL sections: `content` now requires a `target` plus type-specific anchors (`path`, `source|items`, or `destination`), `accounts` require `node`, and `agents` require `entity`.
- Parser boundary tightened: `${var}` placeholders are now rejected in user-defined mapping keys, keeping SDL identifiers concrete at parse/validate time.
- Variable handling expanded: full `${var}` placeholders are now accepted in selected leaf enum-backed property fields (`accounts.password_strength`, `entities.role`, `nodes.os`, `nodes.asset_value.*`, `infrastructure.acls.action`, `objectives.success.mode`) while discriminant `type` fields remain concrete.
- Named service bindings and named ACL rules are now first-class symbolic refs for objectives and relationships via `nodes.<node>.services.<service_name>` and `infrastructure.<infra>.acls.<acl_name>`.
- Added SDL `workflows` section for branching and parallel objective graphs with DAG validation, reachability checks, and objective-window workflow/step binding.
- Moved runtime evaluation models to standalone modules: `aptl.core.objectives` (ObjectiveType, Objective, WazuhAlertValidation, etc.) and `aptl.core.attacks` (AttackStep, MitreReference, ExpectedDetection, etc.). These are runtime evaluation mechanics, distinct from the declarative objective semantics carried by the SDL.
- Removed 6 APTL-specific semantic validation passes from the SDL validator
- Framing: OCR scoring pipeline (conditions → metrics → evaluations → TLOs → goals) stays in the SDL, and the SDL now also carries declarative objectives. Backend-specific automated validation remains a runtime concern outside the language itself.

### Removed

- `src/aptl/core/sdl/compat.py` — legacy backward-compatibility shim
- `src/aptl/cli/scenario.py` — removed with the legacy scenario runtime cutover
- `src/aptl/api/routers/scenarios.py` — removed with the legacy scenario runtime cutover
- `src/aptl/core/engine.py`, `src/aptl/core/evaluators.py`, `src/aptl/core/scoring.py`, `src/aptl/core/run_assembler.py` — removed pending a new SDL-native runtime
- `src/aptl/core/sdl/defenses.py` — free-form defense config (underspecified)
- `src/aptl/core/sdl/attacks.py` — moved to `aptl.core.attacks`
- `tests/test_sdl_compat.py` — tests for removed compat layer

## [5.3.0] - 2026-03-29

### Added

- Property-based fuzz testing for SDL parser (`tests/test_sdl_fuzz.py`) — 6 Hypothesis strategies generating ~1,050 random inputs per run; excluded from default pytest via `fuzz` marker, run with `pytest -m fuzz`
- Real-world scenario stress tests (`tests/test_sdl_realworld.py`) — 6 scenarios from Incalmo/MHBench, NICE Challenge, CCDC, HTB ProLab, Metasploitable 2, and Locked Shields IT/OT/SCADA
- SDL documentation suite (`docs/sdl/`) — index, sections reference, parser behavior, semantic validation (24 passes), design precedents, limitations, and testing guide
- ADR-014: Scenario Description Language architecture decision record
- `hypothesis>=6.0.0` added to dev dependencies

### Changed

- `pyproject.toml` pytest config: added `fuzz` marker and `addopts = "-m 'not fuzz'"` to exclude fuzz tests from default runs

## [5.2.0] - 2026-03-29

### Added

- SDL `relationships` section for typed directed edges between scenario elements — adapted from STIX 2.1 Relationship SROs. Supports `authenticates_with`, `trusts`, `federates_with`, `connects_to`, `depends_on`, `manages`, `replicates_to` with free-form properties dict for type-specific metadata (trust_type, protocol, etc.). This is how identity/directory/trust emerges: accounts describe *who*, features describe *what provides auth*, relationships describe *how they connect*
- SDL `agents` section for autonomous scenario participants — adapted from CybORG CAGE Challenge agent definitions. Each agent has an entity reference (team/role), available actions, starting accounts, initial knowledge (known hosts/subnets), and allowed subnet scope. Agent specifications are framework-agnostic (no Gymnasium coupling)
- SDL `variables` section for scenario parameterization — adapted from CACAO v2.0 playbook_variables. Named variables with types (string/integer/boolean/number), defaults, descriptions, and allowed_values. Variables are stored as `${name}` strings in the model and resolved at instantiation time, not parse time
- Semantic validator passes: `verify_relationships` (source/target resolve to any named element), `verify_agents` (entity/account/subnet/host references resolve), `verify_variables` (structural validation)
- Source expansion now correctly skips `relationships` and `agents` sections where `source` is a string reference, not a package Source
- Stress test scenarios 12 (CybORG CAGE-2 with red/blue/green agents, starting accounts, initial knowledge) and 13 (multi-domain AD with parent-child trust, ADFS federation, and parameterized variables)
- 25 new tests for relationship, agent, and variable models and cross-reference validation

## [5.1.0] - 2026-03-29

### Added

- SDL `content` section for data-in-systems: files, datasets (emails, DB records, pcaps), directory structures placed into scenario nodes — adapted from CyRIS `copy_content` pattern
- SDL `accounts` section for user accounts within scenario nodes: AD users, SSH users, email accounts with password strength, Kerberos SPNs, group memberships — adapted from CyRIS `add_account` and CybORG agent sessions
- Network access control rules (`acls`) on infrastructure nodes — adapted from CybORG `Subnets.NACLs` pattern
- Network `internal` flag on `SimpleProperties` for egress-blocked networks
- Host OS family and version fields on nodes (`os`, `os_version`) — vocabulary from OCSF `Device.os`
- CIA triad `asset_value` on nodes — adapted from CybORG `ConfidentialityValue`/`AvailabilityValue`
- `ServicePort` model for exposed network services on nodes (port/protocol/name) — simplified from OCSF `NetworkEndpoint`
- `PlatformCommand` on attack steps for per-OS command variants with cleanup — adapted from CALDERA `platforms.{os}.{shell}.command` and Atomic Red Team `cleanup_command`
- Health check extensions on conditions: `timeout`, `retries`, `start_period`
- Feature-list shorthand on nodes: `features: [svc-a, svc-b]` expands to dict with empty role binding
- Scenario 11 stress test: Exchange server with mailboxes, accounts, ACLs, phishing lure content, asset values
- 30 new tests for all extension models, validator passes, and shorthand expansion

## [5.0.0] - 2026-03-28

### Added

- Formal Scenario Description Language (SDL) package (`src/aptl/core/sdl/`) ported from the Open Cyber Range SDL, extended for APTL's domain, and decoupled from backend-specific infrastructure
- 14 OCR SDL sections: nodes (VM/Switch), infrastructure (topology/IP/CIDR), features (Service/Configuration/Artifact with cycle-detected dependency graphs), conditions (command/source monitoring), vulnerabilities (CWE-classified), metrics/evaluations/TLOs/goals (full scoring pipeline), entities (recursive org/team/person hierarchy with exercise roles), orchestration (injects/events/scripts/stories with human-readable durations)
- APTL extensions integrated into the SDL: objectives with auto-evaluation (wazuh_alert, command_output, file_exists), attack steps with MITRE ATT&CK mapping and OCSF-aligned expected detections, defense configurations, preconditions, scenario metadata, scoring with time bonuses and hint penalties
- Semantic validator (`validator.py`) with 21 cross-reference passes: node/feature/condition/vulnerability existence checks, infrastructure link and IP/CIDR validation, feature dependency cycle detection, metric/evaluation/TLO/goal reference chains, entity hierarchy validation, inject/event/script/story reference chains, MITRE technique format validation, mode-objective consistency
- SDL parser (`parser.py`) with case-insensitive key normalization, hyphen-to-underscore conversion, shorthand expansion (source strings, infrastructure counts, role strings, min-score integers), and auto-detection of APTL legacy vs OCR SDL format
- Backend-agnostic `Source(name, version)` reference type — no deployment handler coupling
- 117 new tests across 4 test files: structural model validation, semantic cross-reference validation, parser normalization/shorthand/format-detection, and backward compatibility

### Changed

- `src/aptl/core/scenarios.py` is now a backward-compatibility shim re-exporting all names from `aptl.core.sdl` — all 8 consumer files continue working with zero import changes (BREAKING: internal module restructuring)

## [4.17.0] - 2026-03-26

### Added

- Scenario runtime engine with async evaluation loop (`src/aptl/core/engine.py`) — periodically checks non-manual objectives against live infrastructure (Wazuh alerts, command output, file existence) and updates session state incrementally
- Objective evaluators (`src/aptl/core/evaluators.py`) — async functions for each `ObjectiveType`: `evaluate_wazuh_alert` queries the Wazuh Indexer ES API, `evaluate_command_output` runs `docker exec` and checks output, `evaluate_file_exists` checks file presence and content in containers
- Scoring engine (`src/aptl/core/scoring.py`) — computes scenario scores from completed objectives, hint penalties, and time-based bonuses with linear decay
- `aptl scenario run <name>` CLI command — combined start + evaluation loop + stop with Ctrl+C graceful shutdown and real-time progress reporting
- `aptl scenario evaluate` CLI command — single evaluation pass against an active session for debugging and scripting
- `aptl scenario status` now displays current score, objective completion breakdown, and pass/fail status
- `ScenarioSession.set_evaluating()` and `set_active_from_evaluating()` methods — the `EVALUATING` session state is now actively used during evaluation cycles
- OTel `aptl.evaluation` child spans emitted for each objective evaluation cycle
- 45 new tests: evaluator unit tests (`test_evaluators.py`), scoring tests (`test_scoring.py`), engine integration tests (`test_engine.py`), CLI command tests (`test_cli_evaluate.py`)

## [4.16.1] - 2026-03-25

### Fixed
Expand Down
5 changes: 5 additions & 0 deletions docs/adrs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,8 @@ We use [MADR](https://adr.github.io/madr/) (Markdown Any Decision Records). Each
| [009](adr-009-scenario-engine.md) | Scenario Engine with YAML Specs and Run Archive Collectors | accepted | 2026-03-02 |
| [010](adr-010-sonarcloud-quality.md) | SonarCloud for Continuous Code Quality | accepted | 2026-03-07 |
| [011](adr-011-web-ui.md) | Notebook-Style Web UI (SvelteKit + FastAPI) | proposed | 2026-03-20 |
| [012](adr-012-opentelemetry-integration.md) | OpenTelemetry Integration | accepted | 2026-03-21 |
| [013](adr-013-deployment-abstraction.md) | Deployment Backend Abstraction Layer | accepted | 2026-03-22 |
| [014](adr-014-scenario-description-language.md) | Scenario Description Language (SDL) | accepted | 2026-03-29 |
| [015](adr-015-declarative-sdl-objectives.md) | Declarative Experiment Objectives in the SDL | accepted | 2026-03-29 |
| [016](adr-016-workflows-targetable-subobjects-and-enum-variables.md) | Workflows, Targetable Sub-Objects, and Leaf Enum Variables in the SDL | accepted | 2026-03-29 |
2 changes: 2 additions & 0 deletions docs/adrs/adr-007-python-cli-control-plane.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# ADR-007: Python CLI as Primary Control Plane

> Historical note: references to `aptl scenario` in this ADR describe the retired pre-SDL runtime, not the current SDL-only branch.

## Status

accepted
Expand Down
2 changes: 2 additions & 0 deletions docs/adrs/adr-009-scenario-engine.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# ADR-009: Scenario Engine with YAML Specs and Run Archive Collectors

> Historical note: this ADR documents the retired pre-SDL scenario runtime. Its `aptl scenario` commands and runtime modules are not part of the current SDL-only branch.

## Status

accepted
Expand Down
Loading
Loading