test(enclave): assert Coyote Mode auto-ban trigger deterministically (LC-341) #357

Merged
nrupard merged 1 commit from test/lc-341-coyote-trigger into main 2026-06-18 20:37:59 +02:00
Owner

Decision

Yes, the trigger needs an assertion. The Coyote Mode trigger glue in post_message - mode-on AND not-site-admin AND not-enclave-manager AND count_distinct_rooms_posted_recently >= 3 - is security-relevant: a regression silently disables bot protection or, worse, over-bans real users. The DB layer (count/ban/purge) and HTTP enforcement were already tested, but the trigger itself was not, only because it runs in a fire-and-forget spawn.

Change

Extract the fully-gated decision+action from post_message into pub routes::maybe_coyote_ban(state, enclave_id, room_id, user) -> Result<bool> (returns whether it fired). post_message now spawns that for enclave rooms - behaviour is unchanged (same fire-and-forget, same gating), and the coyote-on lookup moves off the request path as a bonus. Re-exported as routes::maybe_coyote_ban (without making the whole room module public) so the integration test can await it directly instead of racing the spawn.

Deterministic tests in routes_coyote_mode.rs:

  • 3 distinct rooms in window -> Ok(true), user banned + last-24h enclave messages soft-deleted.
  • 2 rooms -> Ok(false), not banned (threshold boundary).
  • mode off -> Ok(false).
  • site admin / enclave owner -> Ok(false) (exempt).

The detection/purge SQL stays unit-tested in db_coyote_mode.rs.

Test

just check (clippy std+saas+desktop, fmt), just test, just test-saas all pass. No production behaviour change; no migration.

Closes LC-341.

## Decision **Yes, the trigger needs an assertion.** The Coyote Mode trigger glue in `post_message` - mode-on AND not-site-admin AND not-enclave-manager AND `count_distinct_rooms_posted_recently >= 3` - is security-relevant: a regression silently disables bot protection or, worse, over-bans real users. The DB layer (count/ban/purge) and HTTP enforcement were already tested, but the trigger itself was not, only because it runs in a fire-and-forget spawn. ## Change Extract the fully-gated decision+action from `post_message` into `pub routes::maybe_coyote_ban(state, enclave_id, room_id, user) -> Result<bool>` (returns whether it fired). `post_message` now spawns that for enclave rooms - behaviour is unchanged (same fire-and-forget, same gating), and the coyote-on lookup moves off the request path as a bonus. Re-exported as `routes::maybe_coyote_ban` (without making the whole `room` module public) so the integration test can `await` it directly instead of racing the spawn. Deterministic tests in `routes_coyote_mode.rs`: - 3 distinct rooms in window -> `Ok(true)`, user banned + last-24h enclave messages soft-deleted. - 2 rooms -> `Ok(false)`, not banned (threshold boundary). - mode off -> `Ok(false)`. - site admin / enclave owner -> `Ok(false)` (exempt). The detection/purge SQL stays unit-tested in `db_coyote_mode.rs`. ## Test `just check` (clippy std+saas+desktop, fmt), `just test`, `just test-saas` all pass. No production behaviour change; no migration. Closes LC-341.
test(enclave): assert Coyote Mode auto-ban trigger deterministically (LC-341)
All checks were successful
check-secrets / Nosey parker (pull_request) Successful in 5s
check-secrets / Kingfisher (pull_request) Successful in 9s
Check / clippy + fmt + tests (pull_request) Successful in 3m2s
check-secrets / Kingfisher (push) Successful in 4s
check-secrets / Nosey parker (push) Successful in 4s
check-secrets / TruffleHog (push) Successful in 4s
check-secrets / TruffleHog (pull_request) Successful in 8s
Create release / Create release from merged PR (pull_request) Has been skipped
78a87cba50
Decision: the trigger glue (mode-on / site-admin / enclave-manager / 3-rooms-in-3s gating) is security-relevant - a regression silently disables protection or over-bans - so an assertion is warranted.

Extracts the gated decision+action from post_message into pub routes::maybe_coyote_ban(state, enclave_id, room_id, user) -> Result<bool>, returning whether it fired. post_message now just spawns it for enclave rooms (behaviour unchanged: same fire-and-forget, same gating, and the coyote-on lookup moves off the request path). Re-exported via routes::maybe_coyote_ban so the integration test can await it directly instead of racing the spawn.

Adds deterministic tests: 3 distinct rooms in window -> bans + soft-purges; 2 rooms -> no; mode off -> no; site admin / enclave manager -> exempt. The detection/purge SQL stays unit-tested in db_coyote_mode.rs.

#LC-341

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
nrupard deleted branch test/lc-341-coyote-trigger 2026-06-18 20:37:59 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/lets-chat!357
No description provided.