If you discover a security vulnerability in ragdesk, please report it privately by opening a security advisory. Please do not open a public issue for security problems.
We aim to acknowledge reports within 72 hours.
ragdesk is in active development; security fixes target the main branch.
- Dependabot keeps Go, Python, Docker and GitHub Actions dependencies patched.
- govulncheck scans the Go service for known vulnerabilities on every CI run.
- CodeQL statically analyses the Go and Python code on every push.
Programmatic /v1 keys (rd_live_…) are real secrets, unlike widget keys:
only a SHA-256 hash is stored, the full secret is returned exactly once at
creation, and listings show a display prefix. Keys carry least-privilege
scopes (chat, documents), are limited per key (60 questions/min) on top
of workspace quotas, track last use, and revocation (kept, auditable) cuts
access immediately. Treat a leaked key like a password: revoke it and mint a
new one.
The docs crawler only ever fetches what a workspace member explicitly asked for, and is hardened against SSRF: http/https only; private, loopback, link-local (incl. cloud metadata) and multicast destinations are refused at dial time — after DNS resolution, on every redirect hop — so a hostname pointing at an internal address is caught regardless of spelling; response bodies, timeouts, redirect counts and sitemap fan-out (25 pages) are all bounded. Crawled pages count against the workspace's document cap; the crawler assumes you own the docs you crawl (it does not evaluate robots.txt).
The embeddable widget intentionally works without user auth, so its security model is worth spelling out:
- The widget key is public. It ships in customer page source (
rdw_…) and only names a workspace — possession grants nothing by itself. - The gates are server-side: the browser
Originheader must match the key's allowlist exactly (scheme + host + port, normalized); requests pass a per-IP limit and a per-key budget (60 questions/min) backed by Redis; every question draws down the workspace's monthly plan quota (402 when exhausted); revoking a key (kept, auditable) cuts access immediately. - Thread isolation: widget conversations are stored with
source = 'widget'and the public endpoint can neither read nor extend in-app threads; a widget key never exposes documents, members or billing. - XSS containment:
widget.jsrenders exclusively viatextContentinside a closed shadow root — model output can never inject markup or styles into the host page. - Known limits (accepted): a non-browser client can forge the
Originheader, which is why rate limits and the plan quota — not the origin check — bound abuse; visitor questions are visible to the workspace's members in the conversation list (surface this in your own privacy notice); and, as with any LLM product, retrieved documents plus visitor questions form the prompt, so treat model output as untrusted content — the UI already does.