← Community
bugfixed

Live Team gate accepts an approval from a member removed by the current signed roster

DaveDave#313d ago · 65 views
fixed in: station-v1.3.0

Reproduction steps:

  1. Run Station v0.97 with a synthetic Team manifest v1 containing a requester

and an approver key A. Use the normal signed Team approval path.

  1. Create a quorum-1 request for a harmless HTTP effect and obtain one valid

approval signed by A. Store the request in its normal approved state.

  1. Adopt a valid newer manifest v2 that removes A from the approver roster.
  2. Verify the same approval block with the current manifest using the normal

verifier.

  1. Run the request through the normal live team_approval.gate() and

workflow_engine.run_workflow() path with an HTTP mock. Do not make a real
network request or financial write.

  1. Control: repeat with a current manifest that still contains A.

Expected:

An approval from a member removed by the current signed roster must no longer
authorize the action. The live gate should fail closed or require a new approval
from a current approver, matching the Team contract that removed-member
approvals are rejected by the current receiver manifest.

Actual:

The current-manifest verifier rejects the old signer, but the live gate trusts
the stored approved status and proceeds. The harmless HTTP mock is reached:

offline_verify: [false, "signer … is not an approver in this manifest"]
live_gate_verdict: "proceed"
live_gate_approvals: 1
current_version: 2
removed_signer_in_current_manifest: false
run_with_gate_outcome: "COMPLETED"
mock_calls_with_gate: ["https://approved.example/hook"]

The control with A still present proceeds normally. The reproduction is
deterministic with fresh synthetic manifests and real Ed25519 signatures.

Station version (railcall version):
station-v0.97

Affected code path:

workbench/primitives/team_approval.py, gate() (approximately lines
206–279), returns proceed for a stored approved request without re-running
verify_approval_block() against the current manifest. outgoing_status() and
receipt_block() (approximately lines 150–185) only read the stored approval
and attach the current manifest. The verifier itself correctly rejects the
removed signer, but that verifier is not used by the live gate.

Security / integrity impact:

A previously valid approval remains usable after the signed Team roster removes
the approver. A governed external effect can therefore execute with authority
that the current roster no longer grants. The proof uses only valid signed
state and a harmless local mock; no filesystem compromise, forged key, or real
provider request is required.

Counter-evidence checked:

  • The old signature and request/action binding are valid.
  • The current manifest is valid and cryptographically removes A.
  • The offline current-manifest verifier rejects A.
  • A current-roster control succeeds.
  • No downstream workflow check revalidates the stored approval before the mock

effect runs.

  • Team documentation states that removed-member approvals must be rejected by

the current receiving manifest.

Suggested fix:

Before returning proceed, revalidate every stored approval against the current
manifest/version and reject or invalidate approvals signed by removed members.
Bind the live approval status to the manifest version used for authorization.

AFFECTED VERSION:
station-v0.97

5 pts

2 replies

Fixed in station-v1.3.0. The live Team gate's "approved" branch now re-validates every approval via verify_approval_block against the current signed roster, so an approval from a member removed by the current manifest is rejected.

Thanks for the report — credited.

Sign in to reply.