Use React's canonical "store info from previous renders" pattern:
conditional setState during render resets sidebar when pathname
changes, preventing the sidebar from re-opening on back-navigation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace useRef-based previous pathname tracking (which violated
react-hooks/refs) with a derived state pattern. Sidebar state stores
{open, path} and sidebarOpen is computed as a pure expression,
avoiding both set-state-in-effect and refs-during-render lint errors.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Raise header to z-50 so toggle button stays clickable above backdrop
- Use document keydown listener for Escape instead of onKeyDown on backdrop
- Only render hamburger toggle when signed in
- Make SignedOutPanel col-span responsive (col-span-1 on mobile)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use ref-based previous pathname check instead of useEffect + setState,
which triggers the react-hooks/set-state-in-effect lint rule.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sidebar and backdrop were z-30, same as sticky page headers (live feed,
boards, etc.), causing the hamburger menu to render behind page content.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add hamburger menu with off-canvas sidebar drawer on mobile.
Responsive padding, full-width panels, and stacked layouts
for screens under 768px (md breakpoint).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Restore the existing short token-prefix logging behavior for agent auth failures while keeping the optional bearer-path rate-limit fix. Update tests and docs so the replacement branch reflects the intended logging policy.
Co-Authored-By: Claude <noreply@anthropic.com>
Rate-limit the optional agent bearer path after user auth resolution so mixed user/agent routes no longer leave an unthrottled PBKDF2 path. Stop logging token prefixes on agent auth failures and require a locally supplied token for backend/.env.test instead of committing one.
Update tests and docs to cover agent bearer fallback, configurable webhook signature headers, and the operator-facing security settings added by the hardening work.
Co-Authored-By: Claude <noreply@anthropic.com>
The BASE_URL validation added to Settings breaks the CI migration check
which only sets AUTH_MODE, LOCAL_AUTH_TOKEN, and DATABASE_URL.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use _env_file=None and explicit base_url so tests don't depend on
global env vars or .env file loading.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Always append the timestamp before checking the count so that sustained
spam extends the window, matching the Redis backend's zadd-before-zcard
semantics.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace sync redis.Redis with redis.asyncio to avoid blocking the
event loop during rate-limit checks. Make RateLimiter.is_allowed async
across both backends and update all call sites to await.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Set a9b1c2d3e4f7.down_revision = "a1b2c3d4e5f6" so the activity_events
migration depends on the webhook_secret migration, creating a linear
chain instead of two heads from the same parent.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add get_client_ip() helper that inspects Forwarded and X-Forwarded-For
headers only when the direct peer is in TRUSTED_PROXIES (comma-separated
IPs/CIDRs). Replaces raw request.client.host in rate-limit and webhook
source_ip to prevent all traffic collapsing behind a reverse proxy IP.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Prevents normal user requests with Authorization: Bearer from being
throttled by the agent auth limiter in the shared require_user_or_agent
dependency path.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Strip whitespace (blank → None) and reject non-ASCII-token characters
to prevent impossible header lookups that would fail all signed requests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add RedisRateLimiter using sorted-set sliding window alongside the
existing InMemoryRateLimiter. Users choose via RATE_LIMIT_BACKEND
(memory|redis) with RATE_LIMIT_REDIS_URL falling back to RQ_REDIS_URL.
Redis backend validates connectivity at startup and fails open on
transient errors during requests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Not all webhook providers use X-Hub-Signature-256 or X-Webhook-Signature.
Add an optional signature_header field so users can specify which header
carries the HMAC signature. When set, that exact header is checked;
when unset, the existing auto-detect fallback is preserved. The custom
header is also excluded from stored/exposed payload headers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
get_agent_auth_context_optional was not rate-limited, allowing
brute-force token guessing via routes that use require_user_or_agent.
Now applies agent_auth_limiter when a token is actually presented.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The has_token redaction was reverted to avoid a frontend breaking
change. Update docs to match: tokens are currently returned in API
responses, redaction is planned for a future PR. Also note the
configurable payload size limit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
node:20-alpine uses BusyBox which does not support GNU-style
--system/--ingroup flags. Switch to -S/-G equivalents.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The previous field_validator approach passed `cls` as the first argument
to `_normalize_secret`, which only accepted `v`, causing a TypeError at
runtime. Switch to `Annotated[str | None, BeforeValidator(...)]` which
calls the function with just the value and also eliminates the repeated
validator assignment in both schema classes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move blank/whitespace-only secret normalization to a shared
field_validator on both BoardWebhookCreate and BoardWebhookUpdate.
This ensures consistent behavior across create and update paths
and removes the inline normalization from the endpoint handlers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The has_token boolean redaction requires coordinated frontend changes
(detail page, edit page, orval types). Revert to returning the raw
token for now; token redaction will be handled in a dedicated PR.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
A 6-character prefix of the token is standard practice for debugging
failed auth attempts and is not a security risk. Restored in both
required and optional auth paths, and removed the now-incorrect test
that asserted its absence.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add WEBHOOK_MAX_PAYLOAD_BYTES setting (default 1 MB) so deployments
with larger webhook payloads can raise the limit via environment
variable instead of being hard-blocked at 1 MB.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
_captured_headers was storing all x-* headers including
X-Hub-Signature-256 and X-Webhook-Signature. Since stored headers
are exposed via the payload read endpoint, this enabled replay
attacks without knowing the webhook secret. Now signature and
authorization headers are excluded from capture.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>