Skip to content

automation: the generic run-resume route needs an authorization gate keyed on the suspended node #3801

Description

@os-zhuang

Found while working on #3800. Separate seam, same trust boundary — filing rather
than widening that PR's scope.

Detail is deliberately withheld here, same convention as #3800: the analysis
lives in the private draft advisory under the repo's Security tab. Note it is
reasoned from code, not demonstrated — no repro was written, so treat the
severity as strong-but-unproven until someone does.

Requirement

POST /api/v1/automation/:name/runs/:runId/resume
(packages/runtime/src/domains/automation.ts) forwards a caller-supplied
branchLabel into AutomationEngine.resume. resumeInternal validates
machine state only — the concurrent-resume latch, the run exists, the flow
exists, the suspended node still exists — and nothing about who is asking.

Approval nodes suspend and resume through exactly this mechanism, so the route
needs an authorization gate of its own; today the only thing standing between it
and the approvals rules is convention. The showcase spells that convention out in
a comment ("decide via the approvals API, never a raw engine resume"), and
route-ledger.ts lists the route as SDK-exposed — a comment in an example is not
an access control.

Beyond authorization, a resume that goes around the service also leaves no
sys_approval_action row and no status mirror, so the request row and the run
can end up permanently disagreeing.

Why it is not simply "remove the route"

The route is load-bearing for screen flows: the UI flow-runner posts
{ inputs } to advance a paused screen node, which is its whole reason to
exist. The gate has to discriminate by what the run is parked on, not by the
route — a run suspended on an approval node should be resumable only through
ApprovalService (which records the decision and enforces the slate), while a
screen node keeps working as it does now.

Sketch, for discussion rather than a settled design: have the engine tag a
suspension with the node type that produced it, and have the resume route refuse
a suspension whose owner declares itself service-gated, with the service-side
resume passing an internal marker the route cannot forge.

Refs #3800.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions