|
1 | | -# Security Policy |
| 1 | +# DeepSQL Security |
2 | 2 |
|
3 | | -DeepSQL stores database credentials in an encrypted vault, holds an AES-GCM key |
4 | | -whose loss is unrecoverable, and enforces read-only SQL execution as a guardrail. |
5 | | -We treat reports against those paths as our highest priority. |
| 3 | +## Threat model (be honest) |
6 | 4 |
|
7 | | -## Reporting a vulnerability |
| 5 | +| Posture | Who is trusted | Network | |
| 6 | +|---------|----------------|---------| |
| 7 | +| **Private / single-admin** | One admin (or a fully trusted ops team) | Private network, Tailscale, or SSH tunnel. Prefer only `:3000` reachable from clients. | |
| 8 | +| **Internet multi-user** | Untrusted tenants sharing one install | Requires ACL on every connection-scoped API, hardened compose binds, Actuator lockdown, Hermes behind nginx only, JWT fail-closed — see [`docs/oss-ux/OSS_SECURITY_REVIEW.md`](docs/oss-ux/OSS_SECURITY_REVIEW.md). | |
8 | 9 |
|
9 | | -Report privately via **Security → Report a vulnerability** on this repository. |
10 | | -Do not open a public issue, and do not describe the problem in a pull request. |
| 10 | +DeepSQL is **not** marketed as multi-tenant SaaS until the Criticals in that review are closed and High findings (SSRF, SET allowlist, share-password defaults) are addressed. |
11 | 11 |
|
12 | | -We acknowledge reports within **48 hours**, provide an assessment within |
13 | | -**5 business days**, and aim to ship a fix within **90 days**. We credit |
14 | | -reporters in the published advisory unless you prefer otherwise. |
| 12 | +## Required network layout |
15 | 13 |
|
16 | | -If a report is time-critical and you have had no acknowledgement within 48 hours, |
17 | | -open a public issue containing no technical detail — just a request that a |
18 | | -maintainer check private reports — and we will pick it up. |
| 14 | +```text |
| 15 | +Internet / LAN clients |
| 16 | + │ |
| 17 | + ▼ |
| 18 | + :3000 frontend (nginx) |
| 19 | + ├── /api/* → backend:8080 (auth cookies / JWT) |
| 20 | + └── /agent-api/* → deepsql-agent:8787 (auth_request → /api/auth/me) |
| 21 | + |
| 22 | +Host loopback only (not WAN): |
| 23 | + 127.0.0.1:5432 postgres |
| 24 | + 127.0.0.1:6379 valkey (--requirepass) |
| 25 | + 127.0.0.1:8080 backend (debug / health probes) |
| 26 | + 127.0.0.1:8787 agent API |
| 27 | + 127.0.0.1:8788 agent provisioner |
| 28 | +``` |
19 | 29 |
|
20 | | -## Supported versions |
| 30 | +Do **not** publish Postgres, Valkey, backend, or Hermes on `0.0.0.0` on a cloud VM. |
21 | 31 |
|
22 | | -The latest tagged release receives security fixes. Older tags do not. |
| 32 | +## Secrets checklist |
23 | 33 |
|
24 | | -## In scope |
| 34 | +| Secret | Purpose | |
| 35 | +|--------|---------| |
| 36 | +| `SECURITY_JWT_SECRET` | Session token signing (≥32 bytes). **Required** under `prod` / auth-on — boot fails closed if missing. | |
| 37 | +| `ENCRYPTION_KEY` / `ENCRYPTION_KEYS` | Vault credential encryption | |
| 38 | +| `DB_PASSWORD` | Vault Postgres (never leave as `postgres` on a networked host) | |
| 39 | +| `DEEPSQL_VALKEY_PASSWORD` | Valkey `--requirepass` | |
| 40 | +| `DEEPSQL_CHAT_*` / embedding keys | LLM | |
| 41 | +| `AGENT_PROVISION_SECRET` | Backend ↔ agent provisioner | |
25 | 42 |
|
26 | | -- Credential-vault encryption and key handling |
27 | | -- Read-only SQL execution enforcement, and any bypass of it |
28 | | -- Authentication, JWT handling, and MCP token authorisation |
29 | | -- The admin bootstrap endpoint |
30 | | -- The dashboard sandbox iframe and its read-only query bridge, including the |
31 | | - public share path |
32 | | -- SSH tunnelling |
33 | | -- Reachable dependency vulnerabilities |
| 43 | +`./scripts/self-host/install.sh` generates JWT, encryption, DB, Valkey, bootstrap, and provision secrets when placeholders remain. |
34 | 44 |
|
35 | | -## Out of scope |
| 45 | +## Post-install |
36 | 46 |
|
37 | | -These are by design, and reporting them will get a courteous decline: |
| 47 | +1. Disable admin bootstrap (`SECURITY_ADMIN_BOOTSTRAP_ENABLED=false`) after the first admin exists. |
| 48 | +2. Rotate `ADMIN_BOOTSTRAP_SECRET` if it was ever logged or shared. |
| 49 | +3. Confirm `SPRING_PROFILES_ACTIVE=prod` and `SECURITY_AUTH_ENABLED` is not forced off. |
38 | 50 |
|
39 | | -- Behaviour when `SECURITY_AUTH_ENABLED=false`. This is a development-only |
40 | | - shortcut and is documented as such. |
41 | | -- The hand-written SQL editor's ability to mutate data for a confirming admin. |
42 | | - A DBA tool that cannot run `UPDATE` is not a DBA tool; the guardrail governs |
43 | | - *generated* and *agent-issued* SQL, not a human who has explicitly confirmed. |
44 | | -- The localhost-only bootstrap endpoint when deliberately enabled. |
45 | | -- Anything requiring prior host compromise. |
46 | | -- Missing hardening headers with no demonstrated impact. |
| 51 | +## MCP tokens |
47 | 52 |
|
48 | | -## How fixes are handled |
| 53 | +MCP tokens are **full-account PATs** (same authority as the minting user). Revoke on logout / staff exit. Prefer short-lived tokens; connection-scoped tokens are a roadmap item. |
49 | 54 |
|
50 | | -Fixes are developed in a private fork through GitHub Security Advisories. The |
51 | | -advisory and the patched release are published simultaneously. A vulnerability is |
52 | | -never fixed in a normal public pull request: on a repository anyone can watch, |
53 | | -that commit is a roadmap to the bug for everyone still running the old version. |
| 55 | +## Public dashboard links |
54 | 56 |
|
55 | | -## A note on review requirements |
| 57 | +A public share token authorizes **read-only SQL** against the dashboard’s connection while `is_public` is true. Treat share URLs like credentials; revoke by deleting the share / flipping `is_public`. Prefer password-protected shares once that control ships. |
56 | 58 |
|
57 | | -`.github/CODEOWNERS` routes changes under the vault, authentication and |
58 | | -SQL-execution paths to the security owners, so the right people are *required* |
59 | | -reviewers. It cannot, however, require a larger *number* of approvals on those |
60 | | -paths specifically — GitHub carries a single repo-wide approval count. The |
61 | | -two-approval rule on security-critical paths is therefore a maintainer |
62 | | -convention, enforced by reviewers rather than by the platform. Treat a |
63 | | -security-path pull request carrying only one approval as not yet ready. |
| 59 | +## Reporting vulnerabilities |
64 | 60 |
|
65 | | -## Please do not |
| 61 | +Please report security issues privately to the maintainers (GitHub Security Advisory on [DeepSQLAI/deepsql](https://github.com/DeepSQLAI/deepsql) preferred). Do not open public issues that include exploit details until a fix is available. |
66 | 62 |
|
67 | | -- Test against infrastructure you do not own. |
68 | | -- Include real credentials, API keys, or `ENCRYPTION_KEY` values in a report. |
69 | | - Redact them; we can reproduce from a description. |
| 63 | +## Explicit non-goals (until complete) |
| 64 | + |
| 65 | +- Guaranteeing every legacy controller has connection ACL (track remaining High/Medium in the security review) |
| 66 | +- Scoping MCP tokens to a single connection |
| 67 | +- Hardening every SSRF-capable webhook / LLM endpoint tester |
0 commit comments