← Community
bugfixed

Offline Teams approval-block verifier accepts a quorum of zero signatures

ShwetaShweta#125d ago · 59 views
affected: station-v0.69fixed in: station-v0.70

Reproduction steps:

  1. Mint a valid signed team manifest (team_manifest.mint_root() +

mint_manifest(), one owner member).

  1. Build an approval block: schema=1, matching action_hash and team_id,

approvals: [] (zero signatures), quorum=-1.

  1. Call primitives/team_approval.py::verify_approval_block(action_hash,

block, manifest_doc).

  1. Repeat with quorum="0" (string).

Expected: any quorum below 1, and any block with zero valid approval
signatures, must be rejected.

Actual:
quorum=1 -> ok=False, "only 0 valid approval(s), quorum is 1" (control, correct)
quorum=2 -> ok=False, "only 0 valid approval(s), quorum is 2" (control, correct)
quorum=0 -> ok=False, "only 0 valid approval(s), quorum is 1" (int 0 is falsy, or 1 saves it)
quorum=-1 -> ok=True, "ok" (BYPASS)
quorum="0" -> ok=True, "ok" (BYPASS — string "0" is truthy)

Root cause: workbench/primitives/team_approval.py:483 —
quorum = int(block.get("quorum") or 1)
if len(seen) < quorum: return False, ...
x or 1 only rescues a falsy int 0; a negative int or a truthy non-empty
string like "0" pass through unchanged, then int(-1) or int("0") produce a
quorum the empty-approvals count (0) is not "less than." The identical
pattern exists in the CLI-side verifier (~/.railcall/railcall_cli.py, line
2092: quorum = int(block.get("quorum") or 1)), so both verification paths
share the bug.

Suggested fix:
quorum = int(block.get("quorum") or 1)
if quorum < 1:
return False, f"invalid quorum {quorum!r} — must be >= 1"
if len(seen) < quorum:
...
(apply the same guard in railcall_cli.py's verifier.)

Station version (railcall version): station-v0.69
Module slug + version: Not module-specific; Teams receipt/approval verification
(primitives/team_approval.py::verify_approval_block, and railcall_cli.py
duplicate)

1 reply

Confirmed and fixed in station-v0.70 (station + CLI verifier). Same root cause you nailed: int(quorum or 1) lets -1 and "0" through len(seen) < quorum with zero signatures. Quorum < 1 is now rejected outright.

Note on credit: this was first reported by Dave a little earlier — https://railcall.ai/marketplace/community/offline-teams-approval-verifier-accepts-a-block-with-no-appr-baae1c/ — so the credit for this one lands there. Your independent repro (including the "0" string case and pointing at the CLI duplicate) was spot-on and confirmed it. Thank you.

Sign in to reply.