← Back to PRs

#22744: feat: masked secrets — prevent agents from accessing raw API keys

by theMachineClay open 2026-02-21 16:07 View on GitHub →
agents size: XL
## Summary Implements the masked secrets system proposed in #10659. Agents can **use** API keys via `{{secret:NAME}}` references without ever seeing the plaintext values. Also addresses the output leakage described in #21457 by redacting secrets from stdout/stderr before they reach the agent. ## How it works Three layers of protection in the exec pipeline: 1. **Preflight** — Blocks `env`/`printenv`/`cat .env` and substitutes `{{secret:NAME}}` → real value before execution 2. **Redaction** — Scrubs any leaked secret values from command output (stdout + stderr) before returning to the agent 3. **Pattern matching** — Built-in detection for common API key formats (`sk-ant-*`, `ghp_*`, `AKIA*`) as defense in depth ## Changes **New module** (`src/security/masked-secrets/`): - `types.ts` — Type definitions - `secret-store.ts` — Loads secrets from `.env` + `process.env`, auto-detects common patterns - `substitution.ts` — `{{secret:NAME}}` → real value replacement - `redaction.ts` — Output scrubbing (exact match + pattern match) - `preflight.ts` — Pre-execution command validation - `index.ts` — Public API facade - `INTEGRATION.md` — Integration guide **Wiring** (8 files): - `bash-tools.exec-types.ts` — Added `maskedSecrets` to `ExecToolDefaults` - `bash-tools.exec.ts` — Preflight before exec, redaction after output - `bash-tools.process.ts` — Redaction on poll/log output for background processes - `pi-tools.ts` — Initialize `MaskedSecrets` from config, pass to exec/process tools - `system-prompt.ts` + `pi-embedded-runner/` — Inform agent about `{{secret:NAME}}` syntax - `zod-schema.ts` — Config schema under `security.maskedSecrets` **Tests**: 59 tests (37 unit + 22 integration), all passing. ## Config ```json { "security": { "maskedSecrets": { "enabled": true, "mask": ["OPENAI_API_KEY", "ANTHROPIC_API_KEY", "GITHUB_TOKEN"], "blockSecretFileReads": true } } } ``` ## Design decisions - **Fully optional** — Zero behavior change when `security.maskedSecrets` is not configured. All code paths are no-ops. - **Not a security boundary** — This is a safety net, not a sandbox. A determined attacker could encode secrets to bypass redaction. For true isolation, OS-level enforcement (seccomp, namespaces) is needed. - **Short values excluded** — Values under 8 chars are excluded from exact-match redaction to prevent false positives. Closes #10659 Related: #11829, #21457 <!-- greptile_comment --> <h3>Greptile Summary</h3> This PR implements a masked secrets system that prevents agents from accessing raw API keys through a three-layer protection model: preflight command validation, secret substitution via `{{secret:NAME}}` syntax, and output redaction. The implementation is well-architected with clear separation of concerns across `secret-store.ts`, `substitution.ts`, `redaction.ts`, and `preflight.ts` modules, backed by comprehensive unit and integration tests (59 total). **Key strengths:** - Clean module design with proper TypeScript typing throughout - Comprehensive test coverage including integration tests simulating the full exec pipeline - Thoughtful defaults (blocks `env`/`printenv`, protects `.env` files, auto-detects common API key patterns) - Proper integration into both exec and background process pipelines with consistent redaction - Good documentation in `INTEGRATION.md` **Issues found:** - **Critical**: System prompt receives config mask patterns instead of actual loaded secret names, breaking auto-detection mode functionality - **High**: Regex patterns in `secret-store.ts` match invalid strings (empty strings, single chars) due to using `*` instead of `+` - **Medium**: Missing `os.environ` pattern bypass in preflight checks - **Medium**: Encoding bypasses (`base64`, `xxd`) not blocked for secret file reads - **Low**: Type casting loses type safety in `pi-tools.ts` initialization The core security logic is sound for its intended purpose as a "safety net" (not a security boundary, as documented), but the system prompt integration bug prevents agents from knowing which secrets are available in auto-detection mode. <h3>Confidence Score: 3/5</h3> - This PR has solid architecture and test coverage but contains a critical bug that breaks auto-detection mode - Score reflects well-designed security module with comprehensive tests, offset by critical system prompt integration bug (shows config patterns instead of loaded secrets), regex issues allowing false positive matches, and several bypass scenarios in preflight validation. These issues need resolution before merge to ensure the feature works as intended. - Pay close attention to `src/agents/pi-embedded-runner/compact.ts` and `src/agents/pi-embedded-runner/run/attempt.ts` for the critical system prompt bug, and `src/security/masked-secrets/secret-store.ts` for regex pattern fixes <sub>Last reviewed commit: 9d46e03</sub> <!-- greptile_other_comments_section --> <sub>(3/5) Reply to the agent's comments like "Can you suggest a fix for this @greptileai?" or ask follow-up questions!</sub> <!-- /greptile_comment -->

Most Similar PRs