fix(security): require WEBHOOK_SECRET for the Nextcloud webhook receiver
GHSA-8vh3-g2qg-2h2c (CVSS 9.1, CWE-306): POST /webhooks/nextcloud had no
authentication when WEBHOOK_SECRET was unset (the default). The receiver
trusted the attacker-supplied user.uid and fed it to Qdrant, letting an
unauthenticated network caller delete or re-index any user's vector
embeddings.
Webhooks now require WEBHOOK_SECRET end-to-end:
- app.py: the /webhooks/nextcloud route is only mounted when WEBHOOK_SECRET
is set; otherwise it 404s and a startup warning notes vector sync falls
back to the polling scanner.
- webhook_receiver.py: removed the warn-and-accept fallback. No secret -> 503,
missing/invalid bearer -> 401; the payload is never processed unauthenticated.
- webhook_routes.py / api/webhooks.py: webhook_auth_pair() raises
WebhookSecretNotConfigured instead of returning authMethod="none"; both
registration entry points return a clear 503 so no dead unauthenticated
webhooks are created.
Also expose webhooks availability to the Astrolabe UI via GET /api/v1/status
("webhooks_enabled": bool), set WEBHOOK_SECRET on the docker-compose
semantic-search dev services, and update env.sample + ADR-010 / ADR-018 /
webhook-management-guide docs.
Vector sync still works without a secret via the polling scanner.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
a818b49b3f
commit
4fc2b10945
@@ -722,11 +722,15 @@ class Settings:
|
||||
token_encryption_key: str | None = None
|
||||
token_storage_db: str | None = None
|
||||
|
||||
# Webhook delivery authentication (ADR-010).
|
||||
# When set, the registrar passes Authorization: Bearer <secret> as the
|
||||
# webhook authData and the receiver validates the same header on each
|
||||
# delivery. When unset, registration uses authMethod="none" and the
|
||||
# receiver accepts unauthenticated POSTs (backward-compatible).
|
||||
# Webhook delivery authentication (ADR-010). REQUIRED for webhooks
|
||||
# (GHSA-8vh3-g2qg-2h2c). When set, the registrar passes
|
||||
# Authorization: Bearer <secret> as the webhook authData and the receiver
|
||||
# validates the same header on each delivery. When unset, the
|
||||
# /webhooks/nextcloud route is not mounted, the receiver refuses any request
|
||||
# that reaches it (503), and registration refuses to create webhooks — the
|
||||
# receiver trusts user.uid from the payload, so unauthenticated access would
|
||||
# let any caller delete/re-index other users' embeddings. Vector sync still
|
||||
# works via the polling scanner when this is unset.
|
||||
webhook_secret: str | None = None
|
||||
# Internal URL override for webhook registration. Highest-priority
|
||||
# source for the URL we register with NC (above
|
||||
|
||||
Reference in New Issue
Block a user