You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
automation: the generic run-resume route needs an authorization gate keyed on the suspended node #3801
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.
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
Activity
fix(approvals): an approval action is recorded against the authentica…
[-]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[/+]docs(approvals): the 'never resume directly' rule is now enforced, no…
fix(automation,approvals): gate the generic run-resume route on the s…
fix(automation): resume gate follows `map:` too; the route stops acce…
docs(adr-0044): the revise pause must be service-owned — record why t…
fix(automation): release a wait node's timer job when the run leaves …
fix(approvals): the ADR-0044 revise window is a service-owned node ty…
feat(spec,service-automation,devx): 未声明 resumeAuthority 的 pausing 节点类…
fix(automation): refuse unknown top-level keys on the resume body
fix(automation): the resume body's outer envelope becomes a closed se…
fix(automation): refuse a wait node whose `timerDuration` yields no w…
feat(mcp): resume_run completes a paused screen flow — the caller's o…
fix(spec): os migrate meta guidance for the datasource-*, filter-*, a…