π Before claiming this issue
Two quick steps before you open a PR:
- β Star the repository β low-friction signal that you'll follow through
- π¬ Comment
I'll take this (or similar) below β prevents two contributors racing on the same issue
Full policy: CONTRIBUTING.md β How to claim. If a claim is older than 7 days with no PR, you can reclaim it politely.
Tracks follow-up #5 from PR #17 security review. Blocked on mcp-server-v1 M3 (~Jul 28, 2026).
What
MCPPairingPanel currently allows unlimited pairing attempts. An attacker with access to the panel can brute-force tokens at hundreds of requests/sec. Add client-side rate limiting (token bucket) so failed attempts back off.
Why
Even with timing-safe comparison (C-2) and expiration (C-4), an unlimited attempt rate makes brute-force feasible at scale. The server should also rate-limit (defense in depth), but the client-side rate-limit short-circuits attacks before they reach the network β saving server resources AND signaling "stop trying" UX.
Acceptance criteria
Implementation hint
const MAX_ATTEMPTS = 5;
const WINDOW_MS = 60_000;
const REFILL_INTERVAL_MS = 12_000;
type RateLimitState = {
failures: number;
windowStart: number; // ms epoch
};
function checkRateLimit(state: RateLimitState): { allowed: boolean; retryAfterMs?: number } {
const now = Date.now();
const elapsed = now - state.windowStart;
// Refill: 1 attempt per 12s elapsed
const refilled = Math.min(MAX_ATTEMPTS, state.failures - Math.floor(elapsed / REFILL_INTERVAL_MS));
if (refilled < MAX_ATTEMPTS) {
return { allowed: true };
}
return { allowed: false, retryAfterMs: REFILL_INTERVAL_MS - (elapsed % REFILL_INTERVAL_MS) };
}
UX hint
When the button is disabled, show the countdown via a useState + setInterval:
{rateLimited && (
<p className="error">
Too many attempts. Try again in <span className="countdown">{secondsRemaining}s</span>.
</p>
)}
Context
Tracks follow-up #5 from PR #17 security review. Blocked on
mcp-server-v1M3 (~Jul 28, 2026).What
MCPPairingPanel currently allows unlimited pairing attempts. An attacker with access to the panel can brute-force tokens at hundreds of requests/sec. Add client-side rate limiting (token bucket) so failed attempts back off.
Why
Even with timing-safe comparison (C-2) and expiration (C-4), an unlimited attempt rate makes brute-force feasible at scale. The server should also rate-limit (defense in depth), but the client-side rate-limit short-circuits attacks before they reach the network β saving server resources AND signaling "stop trying" UX.
Acceptance criteria
validate tokenattempts increment a counter stored inchrome.storage.sessionkeyed bypairing_attempts_v1Implementation hint
UX hint
When the button is disabled, show the countdown via a
useState+setInterval:Context