Security¶
Quartermaster is a personal, security-first project. This is its security model and how to report issues.
Reporting a vulnerability¶
Please report privately via GitHub's "Report a vulnerability" (Security advisories) on this repository, rather than a public issue. (No contact address is published in the repo content; note that git commit metadata still carries the author's address.)
Security model¶
- No secrets in the repo.
.gitignoreblocks.env,*.key,*.pem,secrets/, and all DB / state files. CI runs gitleaks over the full git history on every push. - Config is separate from the program (
src/quartermaster/config.py,pydantic-settings): every runtime setting comes from the environment (prefixQM_) or a gitignored.env. Secrets areSecretStr-- masked in logs andrepr, never serialized by accident..env.exampleis generated from the settings model and a test fails if it drifts. - Secrets live in your secret store and are injected via env at runtime. Nothing is read
from plaintext on disk in normal use (
.envis for local dev only). Example with Bitwarden:
export QM_SERPAPI_API_KEY="$(bw get password serpapi)"
export QM_ANTHROPIC_API_KEY="$(bw get password anthropic)"
export QM_IMAP_PASSWORD="$(bw get password mail-app-password)" # only for the imap source
- Fail-safe by default:
QM_DRY_RUNdefaults to true -- the agent never takes a real or binding action unless explicitly disarmed. (Phase-2 bidding additionally requires a second independent arm signal and per-bid human approval.) - No autonomous spending. Phase 1 is search + compare only (no buy button). Phase-2 bidding requires human one-click approval; a reserved-budget ledger enforces a hard cap and releases on every terminal state.
- Network egress is blocked in tests (autouse
tests/conftest.py::block_network): no test can reach the internet or place a real action. - Untrusted input stays data, never instructions. Classifieds/email content is treated as
data; eBay content is processed deterministically and never sent to an LLM. See
docs/plan-final.md(sec.2/sec.4). - Email-input credentials are yours, kept out of the repo. The default
file/stdinsources need none. IMAP uses an app-password (QM_IMAP_PASSWORD, from your secret store) -- that credential is full-mailbox scope, so prefer a dedicated forwarding account. The optional Gmail-API backend caches an OAuth token atdata/gmail_token.json(gitignored, a LIVE credential -- revoke at myaccount.google.com); itsgmail.readonlyis a Google "restricted" scope whose unverified-app refresh token expires ~weekly, which is why IMAP + an app-password is the recommended live path.
Scope / no warranty¶
Personal-use software provided under the MIT License "AS IS", without warranty. You are responsible for your own API keys, any spending, and compliance with each data source's terms of service.