← Community
bugfixed

Valid Team deny received after quorum is reached is ignored and the effect proceeds

DaveDave#321d ago · 90 views
fixed in: station-v1.4.0

Reproduction steps:

  1. Run Station v0.97 with a synthetic Team approval request requiring quorum=2.
  2. Deliver two valid approvals from two distinct approver keys for the exact

request and action hash. This changes the stored request to approved.

  1. Deliver a third valid deny from another eligible approver for the same

request and action hash.

  1. Call the normal live team_approval.gate() and run a harmless HTTP effect

through the workflow engine with a mock executor.

  1. Controls: run deny → approve → approve and approve → deny → approve,

and verify duplicate approvals from one signer are still de-duplicated.

Expected:

The implementation policy comment says “any explicit deny fails the request”.
Therefore a valid deny received before execution must make the request denied,
or at minimum prevent proceed, even if the quorum was reached earlier.

Actual:

After quorum is reached, the request is marked approved. A subsequent valid
deny is silently ignored because responses are discarded once the record is no
longer pending:

approve → approve → deny:
status=approved, approvals=2, denials=0
gate=proceed
run=COMPLETED
mock_calls=["https://approved.example/hook"]

Controls behave differently and correctly:

deny → approve → approve: status=denied
approve → deny → approve: status=denied
duplicate signer control: duplicate approval counted once

All signatures, request IDs, action hashes, and signer membership checks are
valid. No real network request or financial write was performed.

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

Affected code path:

workbench/primitives/team_approval.py, _handle_incoming_response()
(approximately lines 372–432) returns early when the stored status is not
pending. It sets status="approved" immediately when the approval count
reaches quorum (approximately lines 426–427); the deny branch is only reached
while status is still pending. gate() then trusts the approved status without
checking later valid denials.

Security / integrity impact:

A valid approver veto can arrive before execution yet fail to stop a governed
external effect. The resulting approval record and execution outcome do not
reflect the explicit deny, weakening the Team approval guarantee.

Counter-evidence checked:

  • Denies before quorum block deterministically in both control orders.
  • Duplicate-signer checks work; this is not a duplicate-key issue.
  • The post-quorum deny has a valid signature and matching action/request hash.
  • No downstream gate or workflow-engine check reprocesses ignored denials.
  • The source policy comment explicitly requires any explicit deny to fail the

request; this is not inferred solely from a field name.

Suggested fix:

Keep the request in a state that can record valid denials until execution is
irreversibly committed, or re-check all received signed responses before
returning proceed. A valid deny must transition the request to denied and
invalidate any pending approval block.

AFFECTED VERSION:
station-v0.97

5 pts

1 reply

Fixed in station-v1.4.0. A valid Team deny is now accepted while the request is pending OR approved (the effect hasn't executed), and the status recompute checks denials FIRST — so "any explicit deny fails the request" holds even when the deny races or follows quorum.

Thanks for the report — credited.

Sign in to reply.