A sweep is a plan-then-execute fan-out. Planning and synthesis are side-effect free, so you can inspect the whole thing before any credit is spent. Execution is a separate call with its own scope.
Dependency sweeps
One sandbox per package, results synthesized into a sequenced upgrade path.
Plan (free)
POST /api/v1/sweeps/plan — scope loops:write.
{ "packages": ["lodash@4.17.20", "express@4.17.1"], "budgetMicros": 1000000 }
Returns { "plan": { …assignments… } }. 400 packages_required when the list
is empty. Nothing is reserved and no sandbox is created.
Synthesize (free)
POST /api/v1/sweeps/synthesize — scope loops:read.
{ "assignments": [ … ], "results": [ … ] }
Returns { "synthesis": …, "summary": … }. 400 assignments_required when the
assignments are missing.
Run (spends)
POST /api/v1/sweeps/run — scope assessments:create.
{ "packages": ["lodash@4.17.20"], "target": "https://staging.example.com",
"budgetMicros": 1000000, "maxSandboxes": 3 }
Each assignment becomes one ordinary assessment through the same launch path the sandbox page uses, so metering, model routing and the runner protocol are unchanged. Fan-out is bounded by the platform's batch and global limits, and packages past the plan's capacity come back as the next wave rather than disappearing.
| Status | error |
Meaning |
|---|---|---|
| 400 | packages_required |
no packages supplied |
| 400 | no_sweepable_packages |
nothing in the list could be swept; includes the plan |
| 409 | target_not_ready |
no registered target and none passed |
| 402 | insufficient_credits |
balance or per-sandbox budget too low |
Why the scopes differ.
loops:writecan describe work but must not be able to spend. Running a sweep reserves credit and creates sandboxes, so it requiresassessments:create, the same boundary as launching one assessment by hand.
Compromise and privilege-escalation sweeps
This answers a different question from a login-form scan: if an attacker already holds a credential, what does it reach? Cloud control planes, the deploy pipeline, production data, secret stores, and other tenants.
The module never asserts reach it did not observe. An unproven path is
unknown, not reachable.
Assumed footholds
POST /api/v1/compromise/plan takes a profile:
| Profile | Assumes |
|---|---|
developer-laptop |
A local shell as the developer, including their SSO session and CLI caches; credentials in shell history, dotfiles or a checked-out .env; network position inside the corporate VPN if one is in use |
ci-runner |
The runner's build-time secrets and its repository token; the ability to modify build output and any artifact it publishes |
service-account |
A long-lived token for one service with whatever scopes it was granted; no interactive user session |
An unknown profile returns unknown compromise profile "<name>" with no
assignments.
Stages, in attacker order
Every stage is included even when the operator supplies a narrow instruction, because an attacker does not skip a stage that works:
- Harvest additional credentials — cloud CLI config, kubectl contexts, database URLs, SSH keys, browser session stores, CI tokens in dotfiles, secrets in shell history.
- Reach the cloud control plane — read-only enumeration: list roles, read parameter or secret stores, describe instances, check whether a broader role can be assumed. No modification.
- Reach the deploy pipeline — whether the foothold can push to a protected branch, trigger a deploy, alter a build step or write a release artifact.
- Reach production data — prove reach with a scoped read against a non-destructive endpoint, never a write.
- Move laterally, including across tenants — whether the identity reaches another team's or tenant's resources through shared infrastructure, shared secrets, or an over-broad service account.
Reach tiers
Findings land in one of, ordered by implied damage:
production-control, production-data, deploy-pipeline, cloud-control,
internal-only.
Routes and scopes
| Route | Scope | Effect |
|---|---|---|
POST /api/v1/compromise/plan |
loops:write |
free; returns staged assignments |
POST /api/v1/compromise/synthesize |
loops:read |
free; summarizes observed reach |
POST /api/v1/compromise/run |
assessments:create |
spends; dispatches stages in attacker order |
Execution runs one stage at a time. A later stage proceeds only on what the earlier stages actually proved, and each dispatched assessment carries a deterministic idempotency key so a retried run does not double-spend.
Related recipes
critical-dependency-remediation is the recipe that wraps the dependency sweep:
collect advisories, group fixes into patch/minor/major risk bands, open one
branch per group, deploy a preview, launch a focused runtime assessment, verify
the finding in a fresh sandbox, then open a pull request with evidence and
rollback steps.
An agent-driven recipe is an orchestration contract, not a sandbox. The coding agent edits the repository; Offense supplies disposable runtime validation, evidence and retests.