← Community
bugfixed

Team quorum gate skips http DAG nodes and allows governed HTTP effects without approval

DaveDave#321d ago · 26 views
fixed in: station-v0.74

Reproduction steps:

  1. Use a clean RailCall Station v0.71 installation.
  2. Import routes.team and primitives.team_approval.
  3. Monkeypatch primitives.team_approval.gate() to record calls and return:

("pending", {...})

  1. Build a workflow team gate with routes.team.build_workflow_team_gate(ws).
  2. Test two equivalent one-node workflows that perform an external POST.

Effect node:

{
  "id": "n1",
  "type": "effect",
  "provider": "slack",
  "verb": "post_message",
  "action_id": "slack.post_message",
  "args": {}
}

HTTP node:

{
  "id": "n1",
  "type": "http",
  "request": {
    "method": "POST",
    "url": "https://hooks.example.com/...",
    "body": {}
  }
}
  1. Call the generated gate for each workflow and record every call to team_approval.gate().

Expected:

Both external POST operations should be evaluated by the team approval gate. If the policy requires quorum approval for external sends, both workflows should return pending.

Actual:

The effect node is checked by the team gate:

verdict='pending'
team_approval.gate() invoked with=[('slack', 'message_post', 'reversible')]

The HTTP node is not checked:

verdict='proceed'
team_approval.gate() invoked with=[]

The HTTP node returns proceed immediately because it is never enumerated by the team approval gate.

Root cause:

workbench/routes/team.py, inside build_workflow_team_gate(), _iter_actions() recursively handles subworkflow nodes and effect nodes, but has no branch for http nodes.

The workflow engine treats http as a first-class effect node:

  • workflow_engine.py includes HTTP nodes in workflow planning and execution.
  • workflow_http.py:plan_http() classifies mutating HTTP methods as external_send.
  • workflow_http.py:apply_http_plan() can perform the actual network request when live execution is enabled.
  • workflow_mcp.py passes live_http=True when both want_live and allow_live are enabled.

Because _iter_actions() skips http, the team approval policy is never consulted for that node type.

Impact:

A workflow containing a mutating http node can bypass the v0.71 team quorum-approval gate. When live HTTP execution is enabled through the MCP path, the request can reach an external destination without the required team approval decision.

This is an authorization/governance bypass affecting HTTP external effects. No real external request was performed during reproduction.

Suggested fix:

Add an http branch to _iter_actions() that derives the same provider/action class used by workflow_http.plan_http(), for example:

elif k == "http":
    try:
        from workbench import workflow_http as _WH
    except ImportError:
        import workflow_http as _WH

    req = n.get("request") or {}
    plan = _WH.plan_http(
        req,
        n.get("policy"),
        allow_private=n.get("allow_private", False),
    )
    yield (
        "http:" + plan["host"],
        req.get("method", "GET"),
        plan["action_class"],
    )

The approval policy must also define how external_send actions from HTTP nodes are covered by quorum rules.

Relevant source paths:

  • workbench/routes/team.pybuild_workflow_team_gate() / _iter_actions()
  • workbench/workflow_engine.py — HTTP planning and execution
  • workbench/workflow_http.pyplan_http() / apply_http_plan()
  • workbench/workflow_mcp.py — live HTTP execution path

1 reply

Confirmed and fixed — this is the same underlying gap as shweta's earlier report (v0-71-s-new-team-quorum-approval-gate-for-the-dag-effect-pat, filed first): the team quorum gate did not enumerate governed http nodes. Crediting the first report there; the fix (http nodes now enumerated + verb in the dedup key) ships in station-v0.74. Thanks Dave — good independent confirmation.

Sign in to reply.