#23165: fix(security): detect plaintext credentials in security audit
size: M
Cluster:
Error Handling in Agent Tools
## Summary
- **Problem:** `openclaw security audit` only checks 2 credential fields (gateway password, hooks token) but ignores all other plaintext secrets: model provider API keys, channel bot tokens (Telegram, Discord, Slack, Signal, Teams, Mattermost, IRC), TTS API keys, and webhook secrets.
- **Why it matters:** Users get a false sense of security — the audit passes even when API keys worth thousands of dollars sit as plain text in config files.
- **What changed:** Extended `collectSecretsInConfigFindings()` with 5 new finding types that scan all known secret fields in config. Added 13 tests.
- **What did NOT change:** Existing checks (gateway password, hooks token) are untouched. No production code outside the audit module is modified.
## Change Type (select all)
- [x] Bug fix
- [x] Security hardening
## Scope (select all touched areas)
- [x] Auth / tokens
## Linked Issue/PR
- Related #7916 (encrypted API keys / secrets management)
- Related #16663 (SecretsProvider integration)
## New Audit Findings
| Finding ID | Severity | What it detects |
|---|---|---|
| `credentials.plaintext_api_keys` | warn | API keys in `models.providers.*.apiKey` stored as plain text |
| `credentials.plaintext_channel_tokens` | warn | Channel tokens: Telegram botToken, Discord token, Slack botToken/appToken/userToken/signingSecret, Signal password, Teams appPassword, Mattermost password, IRC password |
| `credentials.plaintext_tts_keys` | warn | TTS keys: `tts.elevenlabs.apiKey`, `tts.openai.apiKey` |
| `credentials.plaintext_webhook_secrets` | warn | Webhook secrets: `gateway.controlUi.webhookToken`, `gateway.remote.token`, `gateway.remote.password` |
| `credentials.no_secret_provider` | info | No `secrets.providers` configured while plaintext credentials exist |
All findings include actionable `remediation` text pointing users to env vars or secret provider references.
## Example output
```
$ openclaw security audit
⚠ credentials.plaintext_api_keys (warn)
2 API key(s) stored as plain text in config
Plain-text API keys found at: models.providers.anthropic.apiKey, models.providers.openai.apiKey
Remediation: Use a secret provider reference (${keyring:...}, ${bw:...}, ${op:...}) or ${ENV_VAR}
⚠ credentials.plaintext_channel_tokens (warn)
1 channel token(s) stored as plain text in config
Plain-text channel tokens at: channels.telegram.botToken
Remediation: Move to environment variables or a secret provider.
ℹ credentials.no_secret_provider (info)
No secret provider configured.
Remediation: Add secrets.providers to your config. Options: keyring, bw, op, gcp, aws, env.
```
## Security Impact (required)
- New permissions/capabilities? `No`
- Secrets/tokens handling changed? `No` — audit is read-only, never modifies credentials
- New/changed network calls? `No`
- Command/tool execution surface changed? `No`
- Data access scope changed? `No`
## Evidence
- [x] 13 new tests + 7 existing = 20 total, all passing
- [x] 0 lint errors (oxlint)
- [x] 0 format issues (oxfmt)
- [x] Tests cover: plaintext API keys, env var refs (skipped), secret provider refs (skipped), Telegram/Discord/Slack tokens, TTS keys, webhook secrets, no_secret_provider, empty config, multiple keys
## Human Verification (required)
- Verified: All 20 tests pass locally
- Edge cases: empty config, undefined channels/models/tts, env var references correctly skipped, secret provider references correctly skipped
- What I did **not** verify: Live `openclaw security audit` run (would need a full gateway config)
## Compatibility / Migration
- Backward compatible? `Yes` — new findings are additive, no existing findings changed
- Config/env changes? `No`
- Migration needed? `No`
## Failure Recovery (if this breaks)
- Zero risk — read-only audit function. If it breaks, the audit returns fewer findings (never blocks startup or changes config).
## Risks and Mitigations
- Risk: False positives on provider config values that look like secrets but aren't
- Mitigation: Only checks fields already tagged as `sensitive` in the Zod schema. Uses existing `looksLikeEnvRef()` to exclude proper references.
## AI-Assisted
- [x] This PR was AI-assisted (Claude)
- [x] Fully tested (13 new tests)
- [x] I understand what the code does
Made with [Cursor](https://cursor.com)
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Extended security audit to detect plaintext credentials across all config secret fields (model API keys, channel tokens, TTS keys, webhook secrets). Previously only checked 2 fields (gateway password, hooks token), leaving thousands of dollars in API keys undetected.
**Key changes:**
- Added 5 new finding types covering model provider API keys, channel bot tokens (Telegram/Discord/Slack/Signal/Teams/Mattermost/IRC), TTS API keys, and webhook secrets
- Properly skips env var references (`${VAR}`) and secret provider references (`${bw:...}`, `${keyring:...}`, etc.)
- Added info-level finding when no secret provider is configured but plaintext credentials exist
- 13 new tests covering detection, env refs, secret provider refs, multiple providers, and empty configs
**Implementation quality:**
- Clean helper functions with consistent patterns
- Proper type checking (guards against non-string, non-object values)
- Whitespace handling via `.trim()` throughout
- Backward compatible (additive only, no changes to existing checks)
<h3>Confidence Score: 5/5</h3>
- Safe to merge - read-only audit function with comprehensive tests and no production impact
- High confidence based on: (1) read-only audit code that never modifies config, (2) comprehensive test coverage (13 new tests), (3) proper defensive coding (type checks, trim, guards), (4) backward compatible changes (only extends existing function), (5) well-scoped change matching PR description
- No files require special attention
<sub>Last reviewed commit: 7306b47</sub>
<!-- greptile_other_comments_section -->
<sub>(5/5) You can turn off certain types of comments like style [here](https://app.greptile.com/review/github)!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#19690: fix: security audit suppression, MoE false positive, and hook prefi...
by adityuhkapoor · 2026-02-18
81.2%
#15757: feat(security): add hardening gap audit checks
by saurabhsh5 · 2026-02-13
80.7%
#15794: docs(security): comprehensive security audit report
by kinder-world · 2026-02-13
77.1%
#23174: feat(security): credential leak prevention — exfiltration patterns,...
by ihsanmokhlisse · 2026-02-22
76.8%
#23574: security: P0 critical remediation — plugin sandbox, password hashin...
by lumeleopard001 · 2026-02-22
75.9%
#10514: Security: harden AGENTS.md with gateway, prompt injection, and supp...
by catpilothq · 2026-02-06
75.8%
#23175: feat(security): runtime safety — transcript retention, tool call bu...
by ihsanmokhlisse · 2026-02-22
74.7%
#21053: security(infra): OS keychain storage for device private keys
by richvincent · 2026-02-19
74.6%
#20266: feat: skills-audit — Phase 1 security scanner for installed skills
by theMachineClay · 2026-02-18
74.4%
#17273: feat: add security-guard extension — agentic safety guardrails
by miloudbelarebia · 2026-02-15
74.2%