PavlokPager — Security¶
Threat model (local CLI, single user)¶
PavlokPager holds one secret: a Pavlok auth token that can make the wearer's band vibrate/beep/zap. The realistic threats are:
- Token theft — another local user or a backup/sync process reading the token.
- Token leakage — the secret ending up in shell history, the process list, or logs.
- Abuse loop — a buggy or runaway agent firing zaps repeatedly.
- Log tampering / corruption breaking the safety budget.
Not in scope: network MITM (mitigated by TLS to a fixed host), multi-tenant auth, remote attackers (no listening service).
Controls¶
| Threat | Control | Where |
|---|---|---|
| Token theft at rest | DPAPI (ProtectedData, CurrentUser) on Windows; 0600 file + 0700 dir on Linux/macOS, created with those modes (no chmod race) |
Protect / WriteOwnerOnly / EnsureConfigDir |
| Token leakage via argv | Secrets never positional; hidden prompt or piped stdin only | Setup, LoginAsync, ReadSecret |
| Token leakage via output | Raw API bodies never echoed; status+reason only; full body behind PAVLOK_DEBUG |
LoginAsync |
| Abuse loop | maxPagesPer8Hours hard budget (default 3) → exit 3; maxZap clamp |
PageAsync, SendStimulusAsync |
| Log corruption | Torn/invalid JSONL lines skipped, never thrown | ReadLog |
| Transport | HTTPS only, constant host, per-request Bearer | SendStimulusAsync |
| CI supply chain | GitHub Actions pinned to commit SHAs (not mutable tags) | .github/workflows/security.yml |
Gates run before every push¶
Per house policy, code ships to its private repo only after: (1) a security scan, and (2) the /council-code-review multi-model gate. Results for this repo:
1. Semgrep SAST (Docker)¶
docker run --rm -v "${PWD}:/src" -w /src `
semgrep/semgrep@sha256:06938c1f365d3f67b8cedd8bc117607ae64253f88a0e768e9da9408548927dd6 `
semgrep scan --config p/csharp --config p/security-audit `
--config p/secrets --config p/owasp-top-ten Program.cs
Result: 725 rules, 1 file, 0 findings. Scan record: semgrep-scan.json. Full SARIF re-runs in CI on every push and is uploaded as a build artifact (see .github/workflows/security.yml). CI runs on [self-hosted, home-ci, windows] (moved off ubuntu-latest); the Semgrep image is pinned by content digest rather than the mutable latest tag.
2. Codex OWASP review¶
A focused second-model pass. Found and fixed (commit ed946c7): secrets-on-argv, raw-response echo, plaintext-creation window / directory perms. Marked clean: SSRF, TLS, auth-header handling, unsafe deserialization, path traversal.
3. Council review (Codex + Gemini)¶
Correctness + quality (commit 573ee28): corrupt-log tolerance, shared HttpClient, streamed log read, dry-run log tagging, strict arg validation.
4. Re-audit (org-migration prep, 2026-07-08)¶
Codex OWASP/supply-chain pass over the full tree: CLEAN for hardcoded secrets, injection, and unsafe download/exec. Two supply-chain findings, both fixed: container.image: semgrep/semgrep was an unpinned mutable tag (MEDIUM) — re-pinned by digest; runs-on: ubuntu-latest was a mutable hosted-runner label (LOW) — moved to the fixed self-hosted [self-hosted, home-ci, windows] pool, matching house CI policy. A churn job (5x test-suite repeat, fail on any disagreement) was also added to tests.yml, which was previously missing.
Reporting¶
This is a private, single-user tool. Security concerns → the repo owner (andrewjonesdev).