Surfaced while generalizing the mutation-gate completeness guard in #195.
every_in_scope_mutation_has_its_gate asserts a did_matches( substring is present in a handler body, not that it is the load-bearing enforcement site. submit_bounty (crates/gitlawb-node/src/api/bounties.rs:302-306) binds let is_claimant = ...did_matches(...) on one line and enforces with if !is_claimant { return Forbidden } below it. Deleting the enforcement if leaves the did_matches( marker in place, so the guard stays green, and submit_bounty is a non-repo-scoped POST, so neither the repo-scoped guard nor the mounted-route (GET) completeness gate added in #195 covers it. A regression dropping the claimant check would let any authenticated DID submit any claimed bounty with the suite fully green.
approve_bounty/cancel_bounty place did_matches( inside the enforcement if, so deletion trips the marker, but a wrong-principal swap (creator/claimant) would still pass, since nothing drives their arm.
Fix options:
- Drive
submit/approve/cancel_bounty as single-arm 403 rows in the deny-prober so each arm is executed.
- And/or harden
every_in_scope_mutation_has_its_gate so the required marker is the enforcement expression rather than any substring occurrence.
Surfaced while generalizing the mutation-gate completeness guard in #195.
every_in_scope_mutation_has_its_gateasserts adid_matches(substring is present in a handler body, not that it is the load-bearing enforcement site.submit_bounty(crates/gitlawb-node/src/api/bounties.rs:302-306) bindslet is_claimant = ...did_matches(...)on one line and enforces withif !is_claimant { return Forbidden }below it. Deleting the enforcementifleaves thedid_matches(marker in place, so the guard stays green, andsubmit_bountyis a non-repo-scoped POST, so neither the repo-scoped guard nor the mounted-route (GET) completeness gate added in #195 covers it. A regression dropping the claimant check would let any authenticated DID submit any claimed bounty with the suite fully green.approve_bounty/cancel_bountyplacedid_matches(inside the enforcementif, so deletion trips the marker, but a wrong-principal swap (creator/claimant) would still pass, since nothing drives their arm.Fix options:
submit/approve/cancel_bountyas single-arm 403 rows in the deny-prober so each arm is executed.every_in_scope_mutation_has_its_gateso the required marker is the enforcement expression rather than any substring occurrence.