#22744: feat: masked secrets — prevent agents from accessing raw API keys
agents
size: XL
Cluster:
Security Enhancements and Fixes
## 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
#10514: Security: harden AGENTS.md with gateway, prompt injection, and supp...
by catpilothq · 2026-02-06
77.7%
#7983: feat(security): add secure coding guidelines to system prompt
by TGambit65 · 2026-02-03
76.1%
#15756: [Security]: strip provider apiKey from models.json before prompt se...
by SecBear · 2026-02-13
75.8%
#23174: feat(security): credential leak prevention — exfiltration patterns,...
by ihsanmokhlisse · 2026-02-22
75.6%
#21055: security(cli): gate systemPromptReport behind --debug flag
by richvincent · 2026-02-19
75.1%
#5922: fix(security): add instruction confidentiality directive to system ...
by dan-redcupit · 2026-02-01
74.9%
#16663: feat: GCP Secret Manager integration for external secrets management
by amor71 · 2026-02-15
74.6%
#21136: fix(security): harden agent autonomy controls
by novalis133 · 2026-02-19
74.4%
#22873: fix(tools): enforce global inline-secret blocking for tool inputs
by Kansodata · 2026-02-21
74.2%
#8086: feat(security): Add prompt injection guard rail
by bobbythelobster · 2026-02-03
74.1%