Skip to content

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

Closed

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.

Activity

  1. changed the title [-]Generic `runs/:runId/resume` route bypasses approval authorization entirely (no approver check, no audit row)[/-] [+]automation: the generic run-resume route needs an authorization gate keyed on the suspended node[/+] on Jul 28, 2026
  2. self-assigned this
    on Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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