| Version | Supported |
|---|---|
| latest | ✅ |
If you discover a security vulnerability in Graphium, please report it responsibly.
- Do NOT open a public issue for security vulnerabilities
- Email: kumagallium@gmail.com
- Include:
- Description of the vulnerability
- Steps to reproduce
- Potential impact
- Suggested fix (if any)
This is a volunteer-maintained project, so we cannot guarantee specific response times. That said, we will do our best to:
- Acknowledge your report promptly
- Assess the issue and communicate next steps
- Prioritize fixes based on severity
The following are in scope:
- Cross-site scripting (XSS) in the editor or rendered content
- Injection vulnerabilities
- Data leakage of provenance data or stored API keys
- Escapes from the desktop app sandbox (e.g. arbitrary file write, arbitrary process control via a Tauri command)
The following are out of scope:
- Issues in third-party dependencies (report upstream)
- Attacks requiring physical access to an unlocked device
- Social engineering
Graphium handles research notes and, when AI features are used, third-party LLM API keys. Key measures:
- Content Security Policy — the desktop webview ships with a restrictive
CSP (
src-tauri/tauri.conf.json,app.security.csp).script-srcis'self'only, so injected<script>from note content, imported URLs, or AI output cannot execute.img-src/connect-srcallowhttps:because the app renders arbitrary bookmark/OG images and fetches user-pasted URLs for link previews. - Loopback-only local API — the desktop sidecar and the local server bind
to
127.0.0.1(v0.16.10+; override withGRAPHIUM_BIND_HOST), so the mostly unauthenticated local API is not reachable from other machines. - No central account service — the hosted PWA is a static site with no backend of ours; your data stays in your chosen storage location and your keys stay on your device.
- Constrained desktop commands — the Tauri commands exposed to the webview are scoped to their purpose. Process termination is limited to Graphium's own sidecar process, and file save goes through a native save dialog rather than accepting an arbitrary path from JavaScript.
The desktop app ships its own Node.js runtime and a few hundred npm and Rust packages. These are watched automatically rather than from memory:
| What | How | When |
|---|---|---|
| npm, Rust (Cargo), GitHub Actions, Docker base image | Dependabot security updates open a fix PR when an advisory is published | As advisories appear |
| Production npm dependencies | pnpm audit --prod; high or critical findings open a security issue |
Daily |
| Rust dependencies of the desktop app | cargo audit against the RustSec database |
Daily |
Bundled Node.js (scripts/fetch-node.mjs) |
Compared with Node.js security releases on the same major; also checks that no Node.js major we use (sidecar, Dockerfile, CI) is within 90 days of end of life | Daily |
| Graphium's own code and workflows | CodeQL static analysis | Every pull request and weekly |
The daily checks live in .github/workflows/security-check.yml. An issue they
open is updated in place while the problem lasts and closes itself once the
check is clean, so the open security issues are the current list of known
problems. Fixes ship as patch releases, and the desktop app picks them up
through its auto-updater.
AI features require a third-party LLM API key (Anthropic, OpenAI, a self-hosted endpoint, etc.). Where that key rests depends on how you run Graphium. In every case the key is only ever sent to the LLM provider you configured — never to a Graphium-operated server.
- Keys are stored in the macOS Keychain (service
com.graphium.app, account = model id) via thesecurityCLI, not on disk in plaintext. - On first run, an existing
models.jsonthat still contains a plaintextapiKeyis migrated into the Keychain and the field is stripped from the file.
- The OS-Keychain path is macOS-specific. On Windows and Linux, keys are
currently stored in plaintext in the sidecar's
models.json(<app data>/com.graphium.app/server-data/models.json). - Mitigations we recommend until a platform credential store is wired up:
- Enable full-disk encryption (BitLocker on Windows, LUKS on Linux).
- Use a dedicated, scoped API key with a spending cap for Graphium, so a leaked key has a bounded blast radius and can be revoked independently.
- Wiring Windows Credential Manager / libsecret into the same code path is on the roadmap.
- The PWA has no server of ours. When AI features run against a local/remote
server, the browser holds keys in
localStorage(plaintext) and sends them to that server per request via theX-LLM-API-Keyheader (a passthrough to the upstream LLM API, not authentication for Graphium). - Because a key lives in
localStorage, any XSS on the page origin could read it — this is the main reason the desktop build enforces a CSP and the reason we recommend:- Use a short-lived / disposable API key with a spending cap.
- Serve the app only over origins you trust.
- Docker / self-hosted: the local API has no built-in user auth. Bind it to
loopback or keep it behind your own boundary (VPN/LAN/reverse proxy), and set
GRAPHIUM_AUTH_TOKENif you expose it beyond your own machine. Seedocs/ARCHITECTURE.md§6.1 for the full trust model.
We follow a coordinated disclosure process. Please allow us reasonable time to address the issue before public disclosure.