| 1 | --- |
| 2 | name: secret-handling |
| 3 | description: Never read .env files or write secrets to .squad/ committed files |
| 4 | domain: security, file-operations, team-collaboration |
| 5 | confidence: high |
| 6 | source: earned (issue #267 — credential leak incident) |
| 7 | --- |
| 8 | |
| 9 | ## Context |
| 10 | |
| 11 | Spawned agents have read access to the entire repository, including `.env` files containing live credentials. If an agent reads secrets and writes them to `.squad/` files (decisions, logs, history), Scribe auto-commits them to git, exposing them in remote history. This skill codifies absolute prohibitions and safe alternatives. |
| 12 | |
| 13 | ## Patterns |
| 14 | |
| 15 | ### Prohibited File Reads |
| 16 | |
| 17 | **NEVER read these files:** |
| 18 | - `.env` (production secrets) |
| 19 | - `.env.local` (local dev secrets) |
| 20 | - `.env.production` (production environment) |
| 21 | - `.env.development` (development environment) |
| 22 | - `.env.staging` (staging environment) |
| 23 | - `.env.test` (test environment with real credentials) |
| 24 | - Any file matching `.env.*` UNLESS explicitly allowed (see below) |
| 25 | |
| 26 | **Allowed alternatives:** |
| 27 | - `.env.example` (safe — contains placeholder values, no real secrets) |
| 28 | - `.env.sample` (safe — documentation template) |
| 29 | - `.env.template` (safe — schema/structure reference) |
| 30 | |
| 31 | **If you need config info:** |
| 32 | 1. **Ask the user directly** — "What's the database connection string?" |
| 33 | 2. **Read `.env.example`** — shows structure without exposing secrets |
| 34 | 3. **Read documentation** — check `README.md`, `docs/`, config guides |
| 35 | |
| 36 | **NEVER assume you can "just peek at .env to understand the schema."** Use `.env.example` or ask. |
| 37 | |
| 38 | ### Prohibited Output Patterns |
| 39 | |
| 40 | **NEVER write these to `.squad/` files:** |
| 41 | |
| 42 | | Pattern Type | Examples | Regex Pattern (for scanning) | |
| 43 | |--------------|----------|-------------------------------| |
| 44 | | API Keys | `OPENAI_API_KEY=sk-proj-...`, `GITHUB_TOKEN=ghp_...` | `[A-Z_]+(?:KEY|TOKEN|SECRET)=[^\s]+` | |
| 45 | | Passwords | `DB_PASSWORD=super_secret_123`, `password: "..."` | `(?:PASSWORD|PASS|PWD)[:=]\s*["']?[^\s"']+` | |
| 46 | | Connection Strings | `postgres://user:pass@host:5432/db`, `Server=...;Password=...` | `(?:postgres|mysql|mongodb)://[^@]+@|(?:Server|Host)=.*(?:Password|Pwd)=` | |
| 47 | | JWT Tokens | `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...` | `eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+` | |
| 48 | | Private Keys | `-----BEGIN PRIVATE KEY-----`, `-----BEGIN RSA PRIVATE KEY-----` | `-----BEGIN [A-Z ]+PRIVATE KEY-----` | |
| 49 | | AWS Credentials | `AKIA...`, `aws_secret_access_key=...` | `AKIA[0-9A-Z]{16}|aws_secret_access_key=[^\s]+` | |
| 50 | | Email Addresses | `user@example.com` (PII violation per team decision) | `[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}` | |
| 51 | |
| 52 | **What to write instead:** |
| 53 | - Placeholder values: `DATABASE_URL=<set in .env>` |
| 54 | - Redacted references: `API key configured (see .env.example)` |
| 55 | - Architecture notes: "App uses JWT auth — token stored in session" |
| 56 | - Schema documentation: "Requires OPENAI_API_KEY, GITHUB_TOKEN (see .env.example for format)" |
| 57 | |
| 58 | ### Scribe Pre-Commit Validation |
| 59 | |
| 60 | **Before committing `.squad/` changes, Scribe MUST:** |
| 61 | |
| 62 | 1. **Scan all staged files** for secret patterns (use regex table above) |
| 63 | 2. **Check for prohibited file names** (don't commit `.env` even if manually staged) |
| 64 | 3. **If secrets detected:** |
| 65 | - STOP the commit (do NOT proceed) |
| 66 | - Remove the file from staging: `git reset HEAD <file>` |
| 67 | - Report to user: |
| 68 | ``` |
| 69 | 🚨 SECRET DETECTED — commit blocked |
| 70 | |
| 71 | File: .squad/decisions/inbox/river-db-config.md |
| 72 | Pattern: DATABASE_URL=postgres://user:password@localhost:5432/prod |
| 73 | |
| 74 | This file contains credentials and MUST NOT be committed. |
| 75 | Please remove the secret, replace with placeholder, and try again. |
| 76 | ``` |
| 77 | - Exit with error (never silently skip) |
| 78 | |
| 79 | 4. **If no secrets detected:** |
| 80 | - Proceed with commit as normal |
| 81 | |
| 82 | **Implementation note for Scribe:** |
| 83 | - Run validation AFTER staging files, BEFORE calling `git commit` |
| 84 | - Use PowerShell `Select-String` or `git diff --cached` to scan staged content |
| 85 | - Fail loud — secret leaks are unacceptable, blocking the commit is correct behavior |
| 86 | |
| 87 | ### Remediation — If a Secret Was Already Committed |
| 88 | |
| 89 | **If you discover a secret in git history:** |
| 90 | |
| 91 | 1. **STOP immediately** — do not make more commits |
| 92 | 2. **Alert the user:** |
| 93 | ``` |
| 94 | 🚨 CREDENTIAL LEAK DETECTED |
| 95 | |
| 96 | A secret was found in git history: |
| 97 | Commit: abc1234 |
| 98 | File: .squad/decisions/inbox/agent-config.md |
| 99 | Pattern: API_KEY=sk-proj-... |
| 100 | |
| 101 | This requires immediate remediation: |
| 102 | 1. Revoke the exposed credential (regenerate API key, rotate password) |
| 103 | 2. Remove from git history (git filter-repo or BFG) |
| 104 | 3. Force-push the cleaned history |
| 105 | |
| 106 | Do NOT proceed with new work until this is resolved. |
| 107 | ``` |
| 108 | 3. **Do NOT attempt to fix it yourself** — secret removal requires specialized tools |
| 109 | 4. **Wait for user confirmation** before resuming work |
| 110 | |
| 111 | ## Examples |
| 112 | |
| 113 | ### ✓ Correct: Reading Config Schema |
| 114 | |
| 115 | **Agent needs to know what environment variables are required:** |
| 116 | |
| 117 | ``` |
| 118 | Agent: "What environment variables does this app need?" |
| 119 | → Reads `.env.example`: |
| 120 | OPENAI_API_KEY=sk-... |
| 121 | DATABASE_URL=postgres://user:pass@localhost:5432/db |
| 122 | REDIS_URL=redis://localhost:6379 |
| 123 | |
| 124 | → Writes to .squad/decisions/inbox/river-env-setup.md: |
| 125 | "App requires three environment variables: |
| 126 | - OPENAI_API_KEY (OpenAI API key, format: sk-...) |
| 127 | - DATABASE_URL (Postgres connection string) |
| 128 | - REDIS_URL (Redis connection string) |
| 129 | See .env.example for full schema." |
| 130 | ``` |
| 131 | |
| 132 | ### ✗ Incorrect: Reading Live Credentials |
| 133 | |
| 134 | **Agent needs to know database schema:** |
| 135 | |
| 136 | ``` |
| 137 | Agent: (reads .env) |
| 138 | DATABASE_URL=postgres://admin:super_secret_pw@prod.example.com:5432/appdb |
| 139 | |
| 140 | → Writes to .squad/decisions/inbox/river-db-schema.md: |
| 141 | "Database connection: postgres://admin:super_secret_pw@prod.example.com:5432/appdb" |
| 142 | |
| 143 | 🚨 VIOLATION: Live credential written to committed file |
| 144 | ``` |
| 145 | |
| 146 | **Correct approach:** |
| 147 | ``` |
| 148 | Agent: (reads .env.example OR asks user) |
| 149 | User: "It's a Postgres database, schema is in migrations/" |
| 150 | |
| 151 | → Writes to .squad/decisions/inbox/river-db-schema.md: |
| 152 | "Database: Postgres (connection configured in .env). Schema defined in db/migrations/." |
| 153 | ``` |
| 154 | |
| 155 | ### ✓ Correct: Scribe Pre-Commit Validation |
| 156 | |
| 157 | **Scribe is about to commit:** |
| 158 | |
| 159 | ```powershell |
| 160 | # Stage files |
| 161 | git add .squad/ |
| 162 | |
| 163 | # Scan staged content for secrets |
| 164 | $stagedContent = git diff --cached |
| 165 | $secretPatterns = @( |
| 166 | '[A-Z_]+(?:KEY|TOKEN|SECRET)=[^\s]+', |
| 167 | '(?:PASSWORD|PASS|PWD)[:=]\s*["'']?[^\s"'']+', |
| 168 | 'eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+' |
| 169 | ) |
| 170 | |
| 171 | $detected = $false |
| 172 | foreach ($pattern in $secretPatterns) { |
| 173 | if ($stagedContent -match $pattern) { |
| 174 | $detected = $true |
| 175 | Write-Host "🚨 SECRET DETECTED: $($matches[0])" |
| 176 | break |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | if ($detected) { |
| 181 | # Remove from staging, report, exit |
| 182 | git reset HEAD .squad/ |
| 183 | Write-Error "Commit blocked — secret detected in staged files" |
| 184 | exit 1 |
| 185 | } |
| 186 | |
| 187 | # Safe to commit |
| 188 | git commit -F $msgFile |
| 189 | ``` |
| 190 | |
| 191 | ## Anti-Patterns |
| 192 | |
| 193 | - ❌ Reading `.env` "just to check the schema" — use `.env.example` instead |
| 194 | - ❌ Writing "sanitized" connection strings that still contain credentials |
| 195 | - ❌ Assuming "it's just a dev environment" makes secrets safe to commit |
| 196 | - ❌ Committing first, scanning later — validation MUST happen before commit |
| 197 | - ❌ Silently skipping secret detection — fail loud, never silent |
| 198 | - ❌ Trusting agents to "know better" — enforce at multiple layers (prompt, hook, architecture) |
| 199 | - ❌ Writing secrets to "temporary" files in `.squad/` — Scribe commits ALL `.squad/` changes |
| 200 | - ❌ Extracting "just the host" from a connection string — still leaks infrastructure topology |