#16773: fix(security): OC-100 add rate limiting to pairing code verification
stale
size: S
trusted-contributor
Cluster:
Messaging Platform Improvements
## Summary
- Add in-memory rate limiter to `approveChannelPairingCode` to block brute-force pairing code attempts
- Lock out channel after 10 failed attempts within a 15-minute sliding window
- Reset rate limit counter on successful pairing approval
- Throw descriptive error (`"Too many failed pairing attempts. Try again later."`) when rate-limited
- Add 4 comprehensive test cases for rate limiting behavior
## Security Impact
**OC-100 Medium (CWE-307, Improper Restriction of Excessive Authentication Attempts)** — Attack vectors:
1. **Brute-force pairing codes**: `approveChannelPairingCode` accepted unlimited attempts with no failed-attempt tracking, no lockout, and no rate limiting. An attacker could automate thousands of code guesses per second against the 8-character pairing code space (31^8 ≈ 852B combinations), significantly reducing the effective security of the pairing mechanism.
2. **Automated enumeration**: Without rate limiting, automated scripts could systematically try every possible code within the 1-hour TTL window.
## Changes
| File | Change |
|------|--------|
| `src/pairing/pairing-store.ts` | Add rate limit constants (`PAIRING_MAX_FAILED_ATTEMPTS=10`, `PAIRING_RATE_LIMIT_WINDOW_MS=15min`), in-memory `Map<string, RateLimitEntry>` tracker, `isPairingRateLimited()`, `recordFailedPairingAttempt()`, `resetPairingRateLimit()` helpers, and rate limit check in `approveChannelPairingCode` |
| `src/pairing/pairing-store.test.ts` | Add `"pairing code rate limiting (OC-100)"` test suite with 4 cases: below-threshold allows, lockout after 10 failures, reset on success, per-channel isolation |
## Test plan
- [x] TypeScript compiles with zero errors (`tsc --noEmit`)
- [x] All 8 tests pass (4 existing + 4 new rate limiting tests)
- [x] Linter passes with 0 warnings and 0 errors (`oxlint --type-aware`)
- [x] Below-threshold attempts (9 failures) return `null` without throwing
- [x] 11th failed attempt throws `"Too many failed pairing attempts"`
- [x] Successful pairing resets the counter, allowing new attempts
- [x] Rate limits are isolated per channel (discord lockout doesn't affect signal)
---
*Created by [Aether AI Agent](https://tryaether.ai) — AI security research and remediation agent.*
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Added in-memory rate limiting to pairing code verification to prevent brute-force attacks on 8-character pairing codes. The implementation uses a 15-minute sliding window with a 10-attempt limit per channel. The race condition identified in previous review has been fixed by moving the rate limit check inside the file lock (commit `05dd15ba`). Tests thoroughly cover the rate limiting behavior including threshold enforcement, reset on success, and per-channel isolation.
<h3>Confidence Score: 4/5</h3>
- Safe to merge with minor operational consideration about in-memory state
- The implementation correctly addresses CWE-307 with proper rate limiting inside the file lock to prevent race conditions. Tests are comprehensive. The only consideration is that rate limit state is lost on restart (by design for operational flexibility), which is acceptable for this use case.
- No files require special attention
<sub>Last reviewed commit: 05dd15b</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#23692: fix(pairing): add brute-force protection for DM pairing codes
by widingmarcus-cyber · 2026-02-22
87.1%
#23418: Pairing: add persistent sender and IP backoff controls
by bmendonca3 · 2026-02-22
73.7%
#21697: fix(gateway): unblock local spawn pairing and gated private-LAN ws
by rjuanluis · 2026-02-20
73.3%
#22636: fix(whatsapp): skip pairing store merge when dmPolicy is allowlist (#…
by anillBhoi · 2026-02-21
72.8%
#7911: Unapproved users receive pairing codes on every /start message befo...
by crazypeace · 2026-02-03
71.9%
#17425: fix(gateway): auto-approve scope/role upgrades for already-paired d...
by sauerdaniel · 2026-02-15
71.7%
#22712: fix(gateway): auto-approve all device pairing for localhost connect...
by NewdlDewdl · 2026-02-21
71.6%
#23690: fix(gateway): subagent sessions fail with pairing required on loopb...
by yinghaosang · 2026-02-22
71.3%
#13686: Add opt-in rate limiting and token-based budgets for external API c...
by ShresthSamyak · 2026-02-10
71.2%
#19515: security: add per-connection WebSocket rate limiting
by Mozzzaic · 2026-02-17
71.1%