PYSEC-2026-3866

See a problem?
Import Source
https://github.com/pypa/advisory-database/blob/main/vulns/nextcloud-mcp-server/PYSEC-2026-3866.yaml
JSON Data
https://api.osv.dev/v1/vulns/PYSEC-2026-3866
Aliases
Published
2026-09-10T09:44:56Z
Modified
2026-09-10T12:15:18Z
Severity
  • 9.1 (Critical) CVSS_V3 - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H CVSS Calculator
Summary
nextcloud-mcp-server: Unauthenticated `POST /webhooks/nextcloud` allows arbitrary vector data deletion when `WEBHOOK_SECRET` is unset ( default )
Details

Summary

The POST /webhooks/nextcloud endpoint has no authentication by default: WEBHOOK_SECRET defaults to None and is never required by startup validation. When unset, the receiver accepts any unauthenticated POST. The user_id is taken directly from the attacker-supplied payload and passed to Qdrant, allowing an unauthenticated attacker to delete or corrupt vector embeddings for any user.

Details

Vulnerable file: nextcloud_mcp_server/vector/webhook_receiver.py, function handle_nextcloud_webhook(), lines 55-67

Root cause 1: Auth check is guarded by if secret: - skipped entirely when WEBHOOK_SECRET is unset.

Root cause 2: webhook_secret: str | None = None in config - no startup validator enforces it, even when vector sync is enabled.

Trusted field: payload["user"]["uid"] in webhook_parser.py is used as-is for all Qdrant operations - no cross-check against an authenticated session.

webhook_receiver.py, lines 55-67:

secret = get_settings().webhook_secret  # None by default
if secret:                           # skipped entirely when unset
    ... validate Bearer header ...
else:
    _warn_missing_secret_once()     # just logs, still processes

webhook_parser.py, line 57:

user_id = payload["user"]["uid"]     # attacker-controlled

PoC

No credentials required. Works on any deployment where WEBHOOK_SECRET is not explicitly set (the default).

POST /webhooks/nextcloud
Content-Type: application/json

{
  "event": {
    "class": "OCP\\Files\\Events\\Node\\BeforeNodeDeletedEvent",
    "node": { "path": "/victim/files/Notes/any.md", "id": 12345 }
  },
  "user": { "uid": "victim" },
  "time": 0
}

Result: Qdrant deletes all vector embeddings for victim doc 12345 with no authentication. Attacker can loop over doc IDs for mass deletion. All user targets accepted.

Impact

  • Anyone on the network with access to port 8000 - no credentials needed.
  • Attacker can delete or trigger re-index of any user's vector embeddings in Qdrant by spoofing user.uid in the payload.
  • Mass-sending delete events for all doc IDs destroys the entire semantic search index for all users, requiring a full re-scan to recover.

Recommend Fix

  1. Enforce WEBHOOK_SECRET at startup ( file config_validators.py )
if vector_sync_enabled and not settings.webhook_secret:
    raise ConfigurationError(
        "WEBHOOK_SECRET must be set when vector sync is enabled"
    )
  1. Reject requests when secret is unset ( file webhook_receiver.py )
secret = get_settings().webhook_secret
if not secret:
    return JSONResponse({"status": "unavailable"}, status_code=503)
provided = request.headers.get("authorization", "").encode()
if not hmac.compare_digest(provided, f"Bearer {secret}".encode()):
    return JSONResponse({"status": "unauthorized"}, status_code=401)
References

Affected packages

PyPI / nextcloud-mcp-server

Package

Name
nextcloud-mcp-server
View open source insights on deps.dev
Purl
pkg:pypi/nextcloud-mcp-server

Affected ranges

Type
ECOSYSTEM
Events
Introduced
0 Unknown introduced version / All previous versions are affected
Fixed
0.117.2

Affected versions

0.*
0.17.1
0.65.3
0.65.4
0.66.0
0.66.1
0.66.2
0.67.0
0.68.0
0.68.1
0.68.2
0.68.3
0.68.4
0.69.0
0.70.0
0.70.1
0.70.2
0.70.3
0.70.4
0.71.0
0.72.0
0.72.1
0.72.2
0.72.3
0.72.4
0.72.5
0.72.6
0.72.7
0.73.0
0.73.1
0.73.2
0.74.0
0.75.0
0.75.1
0.75.2
0.76.0
0.77.0
0.77.1
0.78.0
0.79.0
0.79.1
0.79.2
0.79.3
0.80.0
0.81.0
0.82.0
0.83.0
0.83.1
0.83.2
0.83.3
0.83.4
0.84.0
0.84.1
0.84.2
0.85.0
0.85.1
0.86.0
0.86.1
0.86.2
0.86.3
0.86.4
0.87.0
0.87.1
0.87.2
0.88.0
0.88.1
0.88.2
0.88.3
0.89.0
0.90.0
0.90.1
0.90.2
0.91.0
0.91.1
0.91.2
0.91.3
0.92.0
0.92.1
0.93.0
0.94.0
0.94.1
0.95.0
0.96.0
0.97.0
0.98.0
0.98.1
0.99.0
0.100.0
0.101.0
0.101.1
0.101.2
0.101.3
0.101.4
0.102.0
0.103.0
0.104.0
0.104.1
0.105.0
0.106.0
0.107.0
0.108.0
0.108.1
0.108.2
0.108.3
0.109.0
0.109.1
0.110.0
0.110.1
0.110.2
0.111.0
0.112.0
0.113.0
0.113.1
0.114.0
0.115.0
0.115.1
0.116.0
0.117.0
0.117.1

Database specific

source
"https://github.com/pypa/advisory-database/blob/main/vulns/nextcloud-mcp-server/PYSEC-2026-3866.yaml"