Please report suspected vulnerabilities using GitHub's private vulnerability reporting for this repository, rather than a public issue:
https://github.com/ValienteTechnologies/grison/security/advisories/new
This lets us discuss and fix the issue before it's public. Please do not include real client data, hostnames, or credentials in a report — grison's own workspace data model keeps that kind of material local (see "Data handling" below); reproduce with synthetic data instead.
Only the latest published minor version receives security fixes.
| Version | Supported |
|---|---|
latest 0.3.x |
yes |
| anything older | no |
- Credentials live only in
.grison/env, under the workspace's.grison/directory, or inGRISON_*environment variables (which take precedence over the file — for CI/headless use). There is no other place grison reads a token from. .grison/envis writtenchmod 600from the moment it's created (grison/fsio.py'satomic_write_text(..., private=True)— the temp file is created 0600 directly, never 0644-then-chmod, so it's never briefly world/group readable).grison/remote/bootstrap.pyalso tightens the mode of anything already sitting under.grison/on every run..grison/envis never committed:.grison/.gitignoreis a fail-safe allow-list (ignore everything under.grison/except the files that must be tracked —.gitignore,manifest.yml,index.json,SPEC.md,templates/), so a stray edit to the workspace-root.gitignorecan't accidentally leak it.grison validate'sWS-008rule fails if.grison/env,.grison/state/,.grison/snapshots/, or.grison/terms.txtare ever tracked, or ifmanifest.yml/index.jsonare ever git-ignored.- Every Ghostwriter/BookStack base URL must be
https://—grison/remote/http.pyrefuses to construct a client otherwise (HttpConfigError), so a credential is never sent over a plaintext connection. - Grison never logs a credential or bearer token. Request/response logging is not performed at all; the only network-adjacent messages grison prints are sync outcomes (paths, record kinds, counts) and error text, never header or token values.
- A scaffolded
.claude/settings.jsondenies an AI agent'sReadtool on.grison/env,.grison/state/,.grison/snapshots/,.grison/lock, and.grison/terms.txt— see the README's "Agent-proofing" section. - If your Ghostwriter/BookStack deployment sits behind Cloudflare Access, set
GRISON_CF_CLIENT_ID/GRISON_CF_CLIENT_SECRET(both, or neither); the service token pair is sent asCF-Access-Client-Id/CF-Access-Client-Secretheaders on every request.
.grison/state/(per-record sync bases),.grison/snapshots/(undo history, the newest 10 kept),.grison/lock(the workspace lock), and.grison/terms.txt(below) are private and stay on the machine that rangrison sync— none of it is ever committed or sent anywhere but the Ghostwriter/BookStack instance you configured..grison/terms.txtis a private, per-workspace list of confidential terms (e.g. a real client legal name), one per line, optionally restricted to a path prefix (term => findings/reports/14-acme).grison validate'sTXT-002rule fails a document that uses one of these terms outside its allowed prefix, and reports the hit as a line number plus a masked form of the term (first character, then*for the rest) — the term itself is never echoed into output that could be committed or copied elsewhere.- A collision sidecar (
<name>.remote.<ext>) written when a record changed on both sides holds the remote's content, unencrypted, next to the local file. The scaffolded workspace-root.gitignoreignores*.remote.*by design (self-healing, like the.claude/settings.jsondeny-list) — a sidecar is never meant to be committed; resolve it and let the next sync remove it. - grison performs no telemetry and makes no network requests other than to the Ghostwriter/BookStack URLs you configure.