137 lines
6.6 KiB
Markdown
137 lines
6.6 KiB
Markdown
# Secret Obfuscation
|
|
|
|
Prevents sensitive values (API keys, tokens, passwords) from being sent to LLM providers. When enabled, secrets are replaced before outbound text content leaves the process. Reversible obfuscation placeholders are restored when session context is rebuilt for display or resume.
|
|
|
|
## Enabling
|
|
|
|
Disabled by default. Toggle via `/settings` UI or directly in `config.yml`:
|
|
|
|
```yaml
|
|
secrets:
|
|
enabled: true
|
|
```
|
|
|
|
## How it works
|
|
|
|
1. On session startup, secrets are collected from two sources:
|
|
- **Environment variables** whose names match common secret patterns (`KEY`, `SECRET`, `TOKEN`, `PASSWORD`, `PASS`, `AUTH`, `CREDENTIAL`, `PRIVATE`, `OAUTH`) with values >= 8 characters
|
|
- **`secrets.yml` files** (see below)
|
|
|
|
2. Outbound text messages to the LLM have secret values replaced with deterministic placeholders like `#AB12#`, `#AB12:L#`, or `#GITHUBTOKEN_AB12:L#`.
|
|
|
|
3. Session context is deep-walked and obfuscation placeholders are restored when building display/resume context. Replace-mode substitutions are one-way and are not restored.
|
|
|
|
Two modes control what happens to each secret:
|
|
|
|
| Mode | Behavior | Reversible |
|
|
| --------------------- | --------------------------------------------------------------------------------------------- | -------------------------------------------- |
|
|
| `obfuscate` (default) | Replaced with deterministic placeholder `#[A-Z0-9]+(?::[ULCM])?#`, optionally name-prefixed | Yes (deobfuscated in display/resume context) |
|
|
| `replace` | Replaced with deterministic same-length string | No (one-way) |
|
|
|
|
## secrets.yml
|
|
|
|
Define custom secret entries in YAML. Two locations are checked:
|
|
|
|
| Level | Path | Purpose |
|
|
| ------- | -------------------------- | --------------------------- |
|
|
| Global | `~/.omp/agent/secrets.yml` | Secrets across all projects |
|
|
| Project | `<cwd>/.omp/secrets.yml` | Project-specific secrets |
|
|
|
|
Project entries override global entries with matching `content`.
|
|
|
|
### Schema
|
|
|
|
Each entry in the array has these fields:
|
|
|
|
| Field | Type | Required | Description |
|
|
| -------------- | ---------------------------- | -------- | ------------------------------------------------------------- |
|
|
| `type` | `"plain"` or `"regex"` | Yes | Match strategy |
|
|
| `content` | string | Yes | The secret value (plain) or regex pattern (regex) |
|
|
| `mode` | `"obfuscate"` or `"replace"` | No | Default: `"obfuscate"` |
|
|
| `replacement` | string | No | Custom replacement (replace mode only) |
|
|
| `flags` | string | No | Regex flags (regex type only) |
|
|
| `friendlyName` | string | No | Sanitized model-visible label for obfuscate-mode placeholders |
|
|
|
|
### Examples
|
|
|
|
#### Plain secrets
|
|
|
|
```yaml
|
|
# Obfuscate a specific API key (default mode)
|
|
- type: plain
|
|
content: sk-proj-abc123def456
|
|
|
|
# Replace a database password with a fixed string
|
|
- type: plain
|
|
content: hunter2
|
|
mode: replace
|
|
replacement: "********"
|
|
```
|
|
|
|
#### Friendly names
|
|
|
|
`friendlyName` adds semantic context to reversible obfuscation placeholders without exposing the secret value:
|
|
|
|
```yaml
|
|
- type: plain
|
|
content: github_pat_abc123def456
|
|
friendlyName: GitHub Token
|
|
```
|
|
|
|
This produces placeholders shaped like `#GITHUBTOKEN_AB12:L#`. The friendly name is sanitized to uppercase letters and digits, capped at 32 characters, and omitted if it sanitizes to an empty value. Invalid optional `friendlyName` metadata does not disable the secret entry; the secret still obfuscates with an unlabeled placeholder.
|
|
|
|
The hash base is an HMAC of the secret under a private per-install key (stored at `~/.omp/secret-placeholder.key`, never sent to a model), so a transcript reader cannot dictionary the placeholder back to the secret. For non-mixed casing the key is the case-folded value, so casing variants share a base distinguished by a case hint suffix:
|
|
|
|
| Hint | Meaning |
|
|
| ---- | -------------------------------------------- |
|
|
| `:U` | all cased ASCII letters are uppercase |
|
|
| `:L` | all cased ASCII letters are lowercase |
|
|
| `:C` | first cased ASCII letter uppercase, rest lower |
|
|
| `:M` | mixed ASCII casing |
|
|
|
|
`friendlyName` on regex entries labels the configured regex entry, not the matched value. Keep regex labels broad enough to be true for every match.
|
|
|
|
#### Regex secrets
|
|
|
|
```yaml
|
|
# Obfuscate any AWS-style key
|
|
- type: regex
|
|
content: "AKIA[0-9A-Z]{16}"
|
|
|
|
# Case-insensitive match with explicit flags
|
|
- type: regex
|
|
content: "api[_-]?key\\s*=\\s*\\w+"
|
|
flags: "i"
|
|
|
|
# Regex literal syntax (pattern and flags in one string)
|
|
- type: regex
|
|
content: "/bearer\\s+[a-zA-Z0-9._~+\\/=-]+/i"
|
|
```
|
|
|
|
Regex entries always scan globally (the `g` flag is enforced automatically). The regex literal syntax `/pattern/flags` is supported as an alternative to separate `content` + `flags` fields. Escaped slashes within the pattern (`\\/`) are handled correctly.
|
|
|
|
#### Replace mode with regex
|
|
|
|
```yaml
|
|
# One-way replace connection strings (not reversible)
|
|
- type: regex
|
|
content: "postgres://[^\\s]+"
|
|
mode: replace
|
|
replacement: "postgres://***"
|
|
```
|
|
|
|
## Interaction with env var detection
|
|
|
|
Environment variables are collected first, then file-defined entries are appended. File entries can cover secrets that don't live in env vars (config files, hardcoded values, etc.). If the same plain value appears in both env and file entries, the env entry's obfuscate-mode mapping is used first.
|
|
|
|
## Key files
|
|
|
|
- `packages/coding-agent/src/secrets/index.ts` -- loading, merging, env var collection
|
|
- `packages/coding-agent/src/secrets/obfuscator.ts` -- `SecretObfuscator` class, placeholder generation, message obfuscation
|
|
- `packages/coding-agent/src/secrets/regex.ts` -- regex literal parsing and compilation
|
|
- `packages/coding-agent/src/config/settings-schema.ts` -- `secrets.enabled` setting definition
|
|
|
|
## See also
|
|
|
|
- [`auth-broker-gateway.md`](./auth-broker-gateway.md) -- remote credential vault and forward-proxy that keep provider OAuth refresh tokens and access tokens off developer hosts entirely (complementary to in-process obfuscation).
|