Ship safe-op RULE 5: notesPlain-embedded-credential guard (7th leak in 7 weeks)
completedAgent: sergey-engineer
Priority: 1
7th credential leak self-reported 2026-06-22T23:21Z. New shape: `op read op://Fleet Secrets/47llrr4n5vxpk3sqtdgsd52x4m/notesPlain` returned 1Password item notes containing a sample psql connection string that embeds the REAL usr_dev_readonly AWS prod Postgres password as the documentation example. The password is now in #grace-and-bots Slack transcript. Existing safe-op RULES 1-4 do NOT block this shape because `op read` of `notesPlain` has historically been the documented SAFE path (alternative to --reveal). Per 6/12 meta-rule (3+ same-shape recurrences = structural fix REQUIRED same-session): leak #7 in 7 weeks needs same-day fix. The self-report DM said `add to wrapper denylist as a follow-up fleet-task tomorrow` — that violates the 6/10 `opportunistic isnt a plan` rule. Filing NOW from heartbeat instead of tomorrow.
Subtasks (atomic, heartbeat-pickable):
1. DECIDE approach (~15min). Two layers needed: (a) content-pattern scan: when `op read .../notesPlain` is invoked, the wrapper inspects returned content for credential-shaped patterns (psql://user:pass@, postgres://, mysql://, mongodb://, redis://, https://*:*@, literal `psql -h ... -U ... <plaintext>`, literal `password=<value>`, etc.) BEFORE writing to stdout. On match: BLOCK with reason NOTES_EMBEDDED_CREDENTIAL. (b) UUID-level notesPlain denylist: 47llrr4n5vxpk3sqtdgsd52x4m (AWS Postgres usr_dev_readonly) explicitly blocked on notesPlain reads regardless of content match — defense in depth. Audit MEMORY.md UUID table for any other items whose notesPlain has been verified to embed creds.
2. PATCH ~/bin/safe-op (~20min): implement RULE 5. Update RULES header. Preserve RULES 1-4 unchanged.
3. AUDIT-LOG schema (~5min): new reason code NOTES_EMBEDDED_CREDENTIAL.
4. SELF-TESTS (~15min): (a) safe-op read op://Fleet Secrets/47llrr4n5vxpk3sqtdgsd52x4m/notesPlain → BLOCKED (denylist hit). (b) safe-op read op://Fleet Secrets/<safe-uuid>/notesPlain with embedded psql:// in content → BLOCKED (content-pattern hit). (c) safe-op read op://Fleet Secrets/<safe-uuid>/notesPlain with clean notes → ALLOWED. (d) safe-op read op://Fleet Secrets/<uuid>/password → ALLOWED (existing safe shape preserved). (e) RULES 1-4 regression all still pass.
5. UPDATE MEMORY.md UUID table (~10min): mark 47llrr4n... with ⚠️ NOTES_DENYLISTED flag. Add meta-note about notesPlain no longer being universally safe.
6. UPDATE AGENTS.md 🔐 Secret Handling section (~15min): add 7th leak row to table. Add RULE 5 description. Add meta-note: notesPlain is not safe by default — 1Password notes can embed credentials as documentation examples; the wrapper now content-scans before output.
7. UPDATE LEARNINGS.md on next reflection: append 7th-leak entry per 6/12 convention.
8. RAW-OP BYPASS AUDIT (~10min): safe-op audit log last entry is 2026-06-18T20:50Z but the 23:21Z leak occurred via raw op (not through wrapper). Determine if foreground session bypassed wrapper deliberately or by oversight. If raw op was invoked, file follow-up to add shell-level guard (alias / PATH shim) or reinforce AGENTS.md `always invoke safe-op explicitly` contract.
9. CROSS-LINK: LEARNINGS ↔ AGENTS ↔ MEMORY ↔ ~/bin/safe-op ↔ this fleet-task ID.
NOT in scope: rotating usr_dev_readonly — Sergey owns rotation, already prompted in self-report 23:21Z. I owe structural fix.
SOFT DEADLINE: next foreground session that touches safe-op OR tonight reflection ~04:50Z 6/23. Heartbeat can pick up subtasks 1-4 in order if foreground does not.
Event Timeline
created
status_change
queued → in_progress
failed
lease expired — re-queued for retry
in_progress → queued
progress
RULE 5 patched. All 10 tests pass. Audit: bypass was gap-in-rules not raw-op. Completing.
status_change
queued → completed