Please report security problems privately through GitHub's "Report a vulnerability" (Security tab) rather than in a public issue.
-
Keys stay on the server. A key is posted once from the setup dialog, or entered without echo with
intent-sql keys. It is stored in the workspace settings file with owner-only permissions (0600). On Windows, which has no such mode bits, the file is in the per-user%LOCALAPPDATA%\IntentSQLfolder. Keys can also come from environment variables or~/.config/intentsql/providers.env. A key is:- sent only to its own provider, over HTTPS;
- never returned by the API;
- never written to
localStorage, logs, exchanges or benchmark reports.
Provider error messages are shown with the key redacted. Tests check that a saved key never appears in API responses.
-
Local only by default.
intent-sql servelistens on 127.0.0.1. The server accepts only localhostHostheaders, which defends against DNS rebinding. It rejects state-changing requests whose origin is not exactly this server (same host and port), so neither a web page nor another local web app can change settings or spend credits through it. When running the Docker image, publish the port on localhost only (-p 127.0.0.1:7862:7862). -
Read-only data access. Every query runs on a read-only SQLite connection (
mode=ro,query_only), with row and time limits. Generated SQL binds every value as a parameter. Identifiers come only from the database's own schema and are quoted. Imported databases are copied into the workspace, and only the copy is read. -
Custom endpoints. The URL must use HTTPS, except on localhost, and may not contain credentials or a query string. The key is sent only as a bearer token to that endpoint, and redirects are not followed. Changing an endpoint's scheme, host or port drops its saved key, so a key never reaches an address it was not entered for. The
INTENTSQL_URL/INTENTSQL_MODELoverrides never apply to custom models, and an override for a built-in provider must meet the same URL rules. Saved custom models are re-validated when loaded, in case the settings file was edited by hand. Replies are validated before use: a malformed required field makes the model Incompatible, never a crash. -
No hidden fallback. A selected model that does not exist (a typo in
INTENTSQL_PROVIDER, a deleted custom model) selects no model; it never falls through to whichever provider has a key. IntentSQL uses exactly the provider you chose. When it fails, the error names it. Demo mode never stands in for a model. -
Spending guard.
INTENTSQL_BUDGET_USDstops requests once the reported (or, for Liquid, estimated) spend reaches the limit.
CI (.github/workflows/ci.yml) has read-only permissions and uses no secrets. On every push it runs:
- gitleaks over the full Git history (pinned release, checksum-verified).
.gitleaks.tomlallowlists the exact fake keys used by the tests, by value, never by path. - pip-audit on the pinned runtime and development dependencies.
.pre-commit-config.yaml offers the same gitleaks check as a local pre-commit hook.
The 1.0.0 work used a real TypeSafe key for live evaluation. This audit was repeated after that work, without printing the key.
| Check | Result |
|---|---|
| The live key in any Git object (all 890 blobs, commits and tags, on all branches and tags, reachable or not), in full or its first 24 characters | 0 matches |
The live key in the working tree (except the git-ignored .env), evaluation reports, the response cache (4,199 files) and server logs |
0 matches |
| gitleaks 8.21.2, all 59 commits and the working tree | No leaks (only the allowlisted fake test keys) |
| detect-secrets 1.5.0 | Only fake test keys, and the developer's git-ignored .env |
| pip-audit 2.7.3 (pinned runtime and development requirements) | No known vulnerabilities |
| bandit 1.8.6 | No high-severity findings; the same eight medium SQL-string findings, each reviewed: identifiers from the schema, quoted, read-only |
| Wheel, sdist and Docker context | No .env, settings, keys, ledger or reports (checked by listing their contents) |
| Spending | A cumulative ledger capped live calls at $2.50, against a mission cap of $3.00. Actual: $0.79 at the listed price ($0.867 counted by the conservative ledger) |
Scope:
- the whole repository: all 51 commits on all local branches and tags, and all Git objects, including unreachable ones;
- the working tree;
- the built wheel and sdist;
- the Docker build context.
Findings are reported without secret values.
| Check | Tool | Result |
|---|---|---|
| Secrets in Git history | gitleaks 8.21.2 (git mode, default rules) |
No leaks. One finding was a fake test key (tests/test_web.py), now allowlisted by value |
| Real key values in any Git object | a script comparing all 585 blobs, commits and tags with the key values configured on the development machine and with provider key patterns (TypeSafe, Liquid, OpenRouter, generic bearer tokens) | 0 matches |
| Secrets in the working tree | detect-secrets 1.5.0, gitleaks dir |
Only fake test keys, and the developer's own .env, which is git-ignored, untracked, and excluded from the Docker context and packages |
| Packages | inspection of the wheel and sdist contents | No .env, settings or credential files |
| Dependencies | pip-audit 2.7.3 | No known vulnerabilities in the pinned requirements (two development pins were upgraded during the audit) |
| Code | bandit 1.8.6 | No high-severity findings. Eight medium findings (SQL built from strings) were reviewed: each quotes identifiers from the database's own schema and runs read-only. One former case was changed to use quote_identifier |
| History hygiene | git | No stashes and no unreachable objects. A single author identity, whose public address already appears on the published main. The only path to outside material is the relative ../IntentSQL-artifacts |
| Personal data | review | The example databases contain public data (CS50). The test and benchmark fixtures are synthetic. Spider data is not in the repository |
| Licenses | review | Code: MIT. Example databases: CC BY-NC-SA 4.0 (CS50), with a notice in intentsql/data/NOTICE.md and the README. Runtime dependencies (FastAPI, Uvicorn, httpx) are permissively licensed |
| Custom models and key handling | an independent code review with reproductions (fake keys, mock endpoints) | Held up:
tests/test_custom_models.py:
|
Credentials used during development. OpenRouter and OpenJEV keys were provided for development and both were rejected by their providers (expired / invalid). They never appeared in any commit, object, package or report. Deactivating them, and any other credential that was ever used locally, remains the owner's decision. No credential was rotated or revoked as part of this audit, and no history was rewritten.