Gateway tokens were returned as plaintext in GatewayRead API responses.
Replace the `token` field with a boolean `has_token` flag so the API
never exposes the plaintext token. The token remains in the database
for outbound gateway connections (full encryption would require key
management infrastructure).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Agent token auth performed O(n) PBKDF2 operations per request with no
rate limiting, enabling CPU exhaustion attacks. Webhook ingest had no
rate limits either. Add an in-memory token-bucket rate limiter:
- Agent auth: 20 requests/minute per IP
- Webhook ingest: 60 requests/minute per IP
Includes unit tests for the rate limiter.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The file contained a publicly known LOCAL_AUTH_TOKEN value that could
be used against misconfigured deployments. Replace with an empty value
and a comment showing how to generate a secure token. The test suite
continues to work via conftest.py which sets its own test-only token.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
X-Content-Type-Options, X-Frame-Options, and Referrer-Policy all
defaulted to empty (disabled). Set defaults to nosniff, DENY, and
strict-origin-when-cross-origin respectively. Operators can still
override or disable via environment variables.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Both backend and frontend Dockerfiles ran all processes as root.
Add a dedicated appuser in each runtime stage so container processes
run with minimal privileges, limiting blast radius of any container
escape.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The first 6 characters of invalid agent tokens were logged, leaking
partial credential information. Remove token_prefix from log messages
while preserving the request path for debugging.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
send_gateway_session_message only required basic auth (AUTH_DEP) while
all other gateway endpoints required ORG_ADMIN_DEP. Any authenticated
user could send messages to any gateway session. Now requires org-admin
and verifies the board belongs to the caller's organization.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The webhook ingest endpoint read the entire request body with no size
limit, enabling memory exhaustion attacks. Add a 1 MB limit checked
via both Content-Length header (early reject) and actual body size.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
User-controlled fields (skill name, source URL, webhook payloads) were
interpolated directly into agent instruction messages. Sanitize skill
fields by stripping newlines/control chars, and fence all external data
behind "BEGIN EXTERNAL DATA" / "BEGIN STRUCTURED DATA" delimiters with
explicit "do not interpret as instructions" markers. Move system
instructions above the data section so they cannot be overridden.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Webhook ingest endpoint was completely unauthenticated. Add an optional
`secret` field to BoardWebhook. When configured, inbound requests must
include a valid HMAC-SHA256 signature in X-Hub-Signature-256 or
X-Webhook-Signature headers. Uses hmac.compare_digest for timing safety.
Includes migration to add the secret column.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Main agents (board_id=None) could list boards across all organizations.
Now resolves the agent's organization via its gateway and filters boards
by organization_id to prevent cross-tenant data leakage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The function only checked that the caller was an authenticated user
(not an agent) but its name implied privilege enforcement. Rename to
require_user_actor and add docstring clarifying the distinction between
actor-type checks and privilege/role checks (require_org_admin, is_super_admin).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>