Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
18013d2
feat(release-tracks): add STIX bundle output format for snapshot exports
seansica Jul 9, 2026
0db3407
fix(validation-bypasses): make startup rule seeding idempotent
seansica Jul 9, 2026
1b8d3dc
docs: add committable agent guides with local environment split
seansica Jul 9, 2026
e8cf697
test: stabilize the in-memory test database across spec files
seansica Jul 10, 2026
a83c032
feat(release-tracks): add bidirectional refs between objects and tracks
seansica Jul 10, 2026
5a9bfdf
fix(api): reject revision re-keying on in-place updates
seansica Jul 10, 2026
9bccaba
fix(release-tracks): sync candidate and staged pins to new object rev…
seansica Jul 10, 2026
21f6bdd
feat(release-tracks): add into_candidates conflict resolution policy
seansica Jul 10, 2026
1ba8833
refactor(controllers): forward delete errors to the exception middleware
seansica Jul 13, 2026
812d5bc
feat(api): reject in-place mutation of members-pinned revisions
seansica Jul 13, 2026
d4adfc0
fix(release-tracks): capture in-place edits and revocations in revisi…
seansica Jul 13, 2026
ffedbbc
feat(release-tracks): add workflow gate and modified-in-place status
seansica Jul 13, 2026
6ea2606
fix(release-tracks): capture technique conversions in revision sync
seansica Jul 13, 2026
446b017
feat(release-tracks): add track type to workspace.release_tracks back…
seansica Jul 15, 2026
6dc7cea
feat(release-tracks): add releases-by-object lookup
seansica Jul 16, 2026
af58596
fix(release-tracks): enforce cross-tier revision uniqueness
seansica Jul 24, 2026
c1d3ddb
feat(release-tracks): make snapshot retrieval routes explicit
seansica Jul 24, 2026
a890704
feat(release-tracks): unify release operations and previews
seansica Jul 28, 2026
341d80d
feat(release-tracks): complete virtual release workflow
seansica Jul 29, 2026
37f521e
fix(release-tracks): enforce virtual materialization lifecycle
seansica Jul 29, 2026
ab36cdb
feat(release-tracks): resolve virtual quarantine conflicts
seansica Jul 29, 2026
a50a78a
fix(release-tracks): validate virtual composition contracts
seansica Jul 29, 2026
290ba19
fix(release-tracks): validate virtual component identities
seansica Jul 29, 2026
029e33a
fix(release-tracks): validate virtual snapshot schedules
seansica Jul 29, 2026
294e4e5
fix(release-tracks): validate virtual object type filters
seansica Jul 29, 2026
080930f
fix(release-tracks): deduplicate virtual revisions deterministically
seansica Jul 29, 2026
dafdb64
fix(release-tracks): record virtual release provenance
seansica Jul 29, 2026
0c99b73
feat(release-tracks): schedule virtual snapshot materialization
seansica Jul 29, 2026
39d8e94
fix(release-tracks): freeze dynamic staged revisions
seansica Jul 30, 2026
e2dd402
fix(scheduler): recover scheduled virtual snapshots
seansica Jul 30, 2026
7fa26ed
fix(release-tracks): enforce unique release versions
seansica Jul 30, 2026
aacfeff
fix(release-tracks): reject unresolved primary revisions
seansica Jul 30, 2026
173fff2
test(release-tracks): evict dropped dynamic models
seansica Jul 30, 2026
a3734c1
fix(release-tracks): make release protection durable
seansica Jul 30, 2026
256f86e
fix(release-tracks): protect destructive operations
seansica Jul 30, 2026
8730e88
chore(release-tracks): remove pre-release version migration
seansica Jul 30, 2026
86d11ed
feat(release-tracks): make snapshot bundles deterministic
seansica Jul 30, 2026
dd46e43
Merge pull request #487 from mitre-attack/fix/release-tracks-producti…
seansica Jul 30, 2026
9ba3bb7
fix(release-tracks): scope graph migration to active relationships
seansica Jul 30, 2026
9c8ab32
Merge pull request #488 from mitre-attack/fix/deterministic-snapshot-…
seansica Jul 30, 2026
fee3f8b
fix(release-tracks): allow ATT&CK-branded track names
seansica Jul 30, 2026
1cdc18f
feat(release-tracks): accept config when creating tracks
seansica Jul 30, 2026
ee56510
fix(migrations): make canonical domain backfill release agnostic
seansica Jul 31, 2026
2d15fbf
docs(release-tracks): generalize canonical domain repair wording
seansica Jul 31, 2026
054678a
fix(release-tracks): persist scheduled materialization metadata
seansica Jul 31, 2026
bde1bbc
test(release-tracks): verify STIX 2.0 virtual bundles
seansica Jul 31, 2026
8090dbc
fix(release-tracks): preserve materialized virtual graphs
seansica Jul 31, 2026
cfc0371
fix(release-tracks): enable programmatic access to snapshot retrieval
seansica Jul 31, 2026
e351ddf
feat(release-tracks): make deterministic graphs opt in
seansica Aug 4, 2026
0a91329
fix(release-tracks): attest historical snapshot graphs
seansica Aug 4, 2026
9e69977
test(release-tracks): serialize snapshot timestamps in graph URLs
seansica Aug 4, 2026
756a0c2
feat(release-tracks): add editable snapshot notes
seansica Aug 4, 2026
85a011a
fix(release-tracks): close deterministic graphs over members
seansica Aug 4, 2026
b000e01
feat(release-tracks): bound snapshot publication versions
seansica Aug 4, 2026
665876e
feat(release-tracks): persist snapshot bundle hashes
seansica Aug 5, 2026
c2c017c
fix(release-tracks): repair deterministic bundle integrity
seansica Aug 5, 2026
b2ea605
feat(config): expose REST API build information
seansica Aug 7, 2026
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# A place to store artifacts during local development (scripts, datasets, dotenv files, etc.)
.nocommit/**/*

# Machine-specific agent configuration (copy AGENTS.local.example.md to create)
AGENTS.local.md

# Logs
logs
*.log
Expand Down
33 changes: 33 additions & 0 deletions AGENTS.local.example.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# AGENTS.local.md — machine-specific agent configuration

Copy this file to `AGENTS.local.md` (gitignored) and fill in the paths for
your machine. Agents consult this file for local resource locations referenced
by `AGENTS.md`.

## Workbench workspace

Parent directory containing the sibling Workbench repos
(`attack-workbench-frontend`, `attack-workbench-deployment`,
`attack-workbench-taxii-server`, ...):

```
/path/to/workbench/
```

## ADM source checkout

Local clone of https://github.com/mitre-attack/attack-data-model
(Zod schemas under `src/schemas/{sdo,sro,smo,common}`):

```
/path/to/attack-data-model
```

## Bruno API collection

Local Bruno collection mirroring this API (omit this section if you don't
maintain one — agents will then skip the Bruno step in the task workflow):

```
/path/to/bruno/workbench/
```
174 changes: 174 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# ATT&CK Workbench REST API — Agent Guide

Node.js/Express + MongoDB (Mongoose) REST API for managing ATT&CK objects
(STIX 2.x). Part of the multi-repo ATT&CK Workbench ecosystem.

## Related repositories and local environment

Machine-specific absolute paths live in `AGENTS.local.md` at the repo root
(gitignored). If it does not exist, copy `AGENTS.local.example.md` to
`AGENTS.local.md` and fill in the values — or ask the developer. Consult it
before searching the filesystem for any of the resources below.

- **Sibling Workbench repos** — conventionally cloned side-by-side under one
parent directory: `attack-workbench-frontend` (Angular UI that consumes this
API), `attack-workbench-deployment` (Docker Compose configs),
`attack-workbench-taxii-server`. Use them when you need consumer or
deployment context.
- **ADM (ATT&CK Data Model)** — this API validates STIX objects against the
published `@mitre-attack/attack-data-model` package. A local checkout of the
ADM source (`src/schemas/{sdo,sro,smo,common}`) is the authoritative
reference for STIX shapes: valid enum values, required fields, refinements.
Consult it when authoring payloads, especially for regression tests.
- **Bruno API collection** — manual smoke-test requests maintained outside
this repo (see Bruno section below).

## Read the docs first

Before designing or coding, read the relevant docs — they explain the API
surface, system design, and adopted patterns. Do not re-derive them from code:

- `docs/README.md` — index of all documentation
- `docs/user/**` — endpoint behavior and workflows (describes *what is*)
- `docs/developer/**` — architecture and patterns (describes *why and how*),
notably: `data-model.md`, `event-bus-architecture.md`,
`cross-service-reads-pattern.md`, `service-exception-middleware.md`,
`crud-regression-test-taxonomy.md`, and `release-tracks/`
- `CONTRIBUTING.md` — branching and commit conventions

## Architecture

Layered request pipeline; keep new code in the matching layer:

```
app/routes/*-routes.js Express routers + authn/authz middleware.
Auto-mounted by routes/index.js (any *-routes.js).
app/controllers/ Parse & validate requests (Zod), delegate to a
service, forward errors via next(). No business logic.
app/services/ Business logic. meta-classes/base.service.js is the
generic CRUD base (create pipeline: strip
server-controlled fields → generate ATT&CK ID →
compose → ADM-validate → save). Facade pattern for
multi-service domains (e.g. release-tracks-service.js).
app/repository/ Mongo access; _base.repository.js is the generic base.
app/models/ Mongoose schemas. STIX documents have the shape
{ workspace: {...}, stix: {...} }.
```

Key mechanics:

- **Validation is layered**: (1) `express-openapi-validator` against
`app/api/definitions/openapi.yml` (+ `paths/*.yml`, `components/*.yml`);
(2) Zod request schemas in controllers (newer endpoints validate bodies/query
in Zod, with the OpenAPI schema kept loose); (3) ADM validation of the
composed STIX object (`config.validateRequests.withAttackDataModel`).
`work-in-progress` objects use ADM *partial* schemas (fields may be omitted,
but present fields must be valid); all other workflow states use full schemas.
Validation-bypass rules (`/api/config/validation-bypasses`) can suppress
specific ADM errors.
- **Every query parameter must be declared in the OpenAPI paths YAML** or the
validator rejects the request. Comma-separated list params need
`allowReserved: true` and a loose `oneOf` string/array schema, with real
validation in Zod.
- **Server-controlled fields**: on create, the server strips client-supplied
ATT&CK external references and `workspace.attack_id` (then generates them),
strips `revoked` and `x_mitre_attack_spec_version`, and stamps
`created_by_ref` with the organization identity.
- **Event-driven architecture**: cross-service *writes* must go through the
EventBus (`app/lib/event-bus.js`); direct repository *reads* across services
are permitted (see `cross-service-reads-pattern.md`).
- **Errors**: throw typed exceptions from `app/exceptions`; centralized
handlers in `app/lib/error-handler.js` map them to HTTP responses.
- **Config**: convict-based, `app/config/config.js`, env-var driven.

## Commands

```bash
npm run lint # eslint (includes prettier rules)
npm run format # prettier + eslint --fix
npm run test:file -- <path/to/spec.js> # one spec file
npm run test:api # all API regression tests (~1-2 min)
npm test # full suite: openapi + config + api + middleware
```

Tests use `mongodb-memory-server` — no external MongoDB or env setup needed.

## Task workflow

1. **Plan in a committable scratchpad**: track multi-step work as checkboxes in
`docs/developer/TODO.md` so progress survives context-window resets and
sessions. Check items off as they complete. Throwaway artifacts (notes,
datasets, one-off scripts) go in `.nocommit/` (gitignored).
2. **Definition of done** — a task is complete only when it includes:
- implementation,
- regression tests (see below),
- test verification, strictly in this order: run the relevant spec files
with `npm run test:file -- <path>` while iterating, then run the **full**
`npm test` suite — all of it must pass before the task is done,
- OpenAPI spec updates for any API-surface change,
- documentation updates (`docs/user/**` = what the behavior *is*;
`docs/developer/**` = why/how, including how behavior evolved),
- Bruno collection updates for any API-surface change,
- a proposed conventional commit message.
3. **Commits**: conventional commits are enforced (commitlint +
semantic-release; see `CONTRIBUTING.md`). Propose the message (type(scope):
imperative subject + body); do not run `git commit` unless asked. Put
unrelated fixes discovered along the way in their own commit.

## Writing regression tests

Follow the existing pattern in `app/tests/api/<area>/*.spec.js` (mocha +
supertest + expect; see `docs/developer/crud-regression-test-taxonomy.md`):

- `before()`: `database.initializeConnection()` →
`databaseConfiguration.checkSystemConfiguration()` → set
`config.validateRequests` flags → `initializeApp()` → `login.loginAnonymous()`.
- **Always enable ADM validation** (`config.validateRequests.withAttackDataModel
= true`) and make payloads ADM-valid — check the ADM Zod sources when unsure.
Common traps: `kill_chain_phases[].kill_chain_name` must be
`mitre-attack` / `mitre-mobile-attack` / `mitre-ics-attack`;
`x_mitre_platforms` must use real platform names (e.g. `Windows`).
- Account for server-controlled fields: read generated values
(`workspace.attack_id`, `stix.created_by_ref`, ATT&CK external refs) from the
POST response rather than asserting on what you sent. To simulate states the
API won't accept on create (`revoked`, missing ATT&CK ID), update the
document directly via the Mongoose model.
- Startup seeds four static marking definitions (e.g. TLP:WHITE
`marking-definition--613f2e26-407d-48c7-9eca-b8e91df99dc9`) and a placeholder
organization identity; the MITRE identity is *not* seeded.

## Bruno smoke tests

The Bruno collection (location in `AGENTS.local.md`) mirrors the API for
manual testing — one `.bru` file per request, grouped in folders, environments
in `environments/`. When changing the API surface, update the affected `.bru`
files: keep the `url` line consistent with enabled `params:query` entries, add
new optional params as disabled toggles (`~name: value`), and document
parameter semantics in the `docs { }` block.

## Gotchas

- STIX version rules: the bundle envelope carries `spec_version` only in STIX
2.0 (2.1 removed it; each 2.1 *object* declares its own `spec_version`).
Marking definitions have no `stix.modified`.
- `p-limit` is not a dependency and recent versions are ESM-only — use a small
inline concurrency runner instead.
- Legacy endpoints under deprecation (e.g. `GET /api/stix-bundles`) are
replaced by release-tracks equivalents — check
`docs/developer/release-tracks/bundle-export.md` before extending them.
- Historic full-suite flake (fixed 2026-07-10): per-spec-file mongod
restarts hit "Port already in use", failing a random file's `before` hook
(visible as `loginAnonymous` 404s). `database-in-memory.js` now reuses one
mongod across spec files and the mocha scripts use `--exit`. If roaming
single-file failures reappear, re-run that spec file in isolation before
treating them as real, and check mongod startup errors at the top of the
run output.

## Maintaining this guide

Treat this file like code. At the end of a task, consider whether a durable,
non-obvious lesson was learned (a validation trap, a pattern decision, a
workflow correction) and propose adding it here; prune entries that are stale
or no longer earn their token cost — this file is loaded into every agent
session. Machine-specific paths never belong in this file; they go in
`AGENTS.local.md`.
8 changes: 8 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# CLAUDE.md

The canonical agent guide for this repository is AGENTS.md (shared across all
coding agents; edit that file, not this one). Machine-specific paths live in
AGENTS.local.md (gitignored; copy from AGENTS.local.example.md if missing).

@AGENTS.md
@AGENTS.local.md
Loading
Loading