Files
envsitter-guard/README.md
David Ibia fa23dd07f8 feat: add secure envsitter dotenv operations
Expose validate/copy/format/annotate tools with dry-run by default, and switch is_equal matching to candidate hashing. Remove prompt-append warning to avoid writing into OpenCode input.
2026-01-13 19:28:39 +01:00

294 lines
8.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# envsitter-guard
OpenCode plugin that prevents agents/tools from reading or editing sensitive `.env*` files, while still allowing safe inspection via EnvSitter (keys + deterministic fingerprints; never values).
## Quickstart (OpenCode)
OpenCode supports loading plugins from npm or local plugin files.
Reference docs:
- https://opencode.ai/docs/plugins/
- https://opencode.ai/docs/config/#plugins
### Option A (recommended): load from npm via `opencode.json`
Add the plugin package to your OpenCode config.
`opencode.json`:
```json
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["envsitter-guard@latest"]
}
```
Restart OpenCode after updating config.
## Why
Accidentally printing `.env` contents is one of the easiest ways for an agentic workflow to leak secrets into:
- chat transcripts
- logs/tool output
- commits/patches
- screenshots / shared sessions
`envsitter-guard` blocks risky operations and points you to safe alternatives.
## What it does
This plugin provides safe EnvSitter-backed tools and blocks sensitive file access via OpenCode tool hooks.
### Safe tools (no values)
These tools never return raw `.env` values:
- `envsitter_keys`: list keys in a dotenv file
- `envsitter_fingerprint`: deterministic fingerprint of a single keys value
- `envsitter_match`: boolean/shape checks and outside-in candidate matching (without printing values)
- `envsitter_match_by_key`: bulk candidate-by-key matching (returns booleans only)
- `envsitter_scan`: scan value *shapes* (jwt/url/base64) without printing values
- `envsitter_validate`: validate dotenv syntax (no values; issues only)
- `envsitter_copy`: copy keys between env files (no values; plan + line numbers only)
- `envsitter_format` / `envsitter_reorder`: reorder/format env files (no values)
- `envsitter_annotate`: add comments near keys (no values)
Notes for file operations:
- File operations are dry-run unless `write: true` is provided.
- Tools only return keys, booleans, and line numbers/operation plans.
### Blocking behavior
- Sensitive paths: `.env`, `.env.local`, `.env.production`, etc. (`.env*`)
- Allowed: `.env.example`
- Always blocked: `.envsitter/pepper`
Blocked operations via tool hooks:
- `read` on sensitive `.env*` paths
- `edit` / `write` / `patch` / `multiedit` on sensitive `.env*` paths
When blocked, the plugin shows a throttled warning toast and suggests using EnvSitter tools instead.
## Tools
Tools only operate on `.env`-style files inside the current project.
- Most tools accept a `filePath` that defaults to `.env`.
- File operations are dry-run unless `write: true` is provided.
### `envsitter_keys`
Lists keys in a dotenv file.
- Input: `{ "filePath"?: string, "filterRegex"?: string }`
- Output: JSON `{ file, keys }`
Example (inside OpenCode):
```json
{ "tool": "envsitter_keys", "args": { "filePath": ".env" } }
```
Optional filtering:
```json
{ "tool": "envsitter_keys", "args": { "filePath": ".env", "filterRegex": "/^(API_|DB_)/" } }
```
### `envsitter_fingerprint`
Computes a deterministic fingerprint of a single key.
- Input: `{ "filePath"?: string, "key": string }`
- Output: JSON `{ file, key, result }`
Example (inside OpenCode):
```json
{ "tool": "envsitter_fingerprint", "args": { "filePath": ".env", "key": "DATABASE_URL" } }
```
### `envsitter_match`
Matches key values without printing them.
- Input:
- `{ "filePath"?: string, "op"?: string, "key"?: string, "keys"?: string[], "allKeys"?: boolean, "candidate"?: string, "candidateEnvVar"?: string }`
- Output:
- If `key` provided: JSON `{ file, key, op, match }`
- If `keys` or `allKeys`: JSON `{ file, op, matches }`
Notes:
- Provide exactly one selector: `key`, `keys`, or `allKeys: true`.
- For ops that compare against a candidate (`is_equal`, `partial_match_*`), provide `candidate` or `candidateEnvVar`.
Examples (inside OpenCode):
```json
{ "tool": "envsitter_match", "args": { "filePath": ".env", "key": "SENTRY_DSN", "op": "exists" } }
```
```json
{ "tool": "envsitter_match", "args": { "filePath": ".env", "key": "PORT", "op": "is_number" } }
```
```json
{ "tool": "envsitter_match", "args": { "filePath": ".env", "key": "NODE_ENV", "op": "is_equal", "candidate": "production" } }
```
### `envsitter_match_by_key`
Bulk match candidates-by-key without printing values (returns booleans only).
- Input:
- `{ "filePath"?: string, "candidatesByKey"?: Record<string,string>, "candidatesByKeyJson"?: string, "candidatesByKeyEnvVar"?: string }`
- Output: JSON `{ file, matches }`
Note: provide exactly one of `candidatesByKey`, `candidatesByKeyJson`, or `candidatesByKeyEnvVar`.
Example (inside OpenCode):
```json
{ "tool": "envsitter_match_by_key", "args": { "filePath": ".env", "candidatesByKey": { "NODE_ENV": "production" } } }
```
### `envsitter_scan`
Scan value shapes (jwt/url/base64) without printing values.
- Input: `{ "filePath"?: string, "detect"?: ("jwt"|"url"|"base64")[], "keysFilterRegex"?: string }`
- Output: JSON `{ file, findings }`
Example (inside OpenCode):
```json
{ "tool": "envsitter_scan", "args": { "filePath": ".env", "detect": ["jwt", "url"] } }
```
### `envsitter_validate`
Validate dotenv syntax.
- Input: `{ "filePath"?: string }`
- Output: JSON `{ file, ok, issues }`
Example (inside OpenCode):
```json
{ "tool": "envsitter_validate", "args": { "filePath": ".env" } }
```
### `envsitter_copy`
Copy keys between env files. Output includes a plan (keys + line numbers), never values.
- Input:
- `{ "from": string, "to": string, "keys"?: string[], "includeRegex"?: string, "excludeRegex"?: string, "rename"?: string, "onConflict"?: "error"|"skip"|"overwrite", "write"?: boolean }`
- Output: JSON `{ from, to, onConflict, willWrite, wrote, hasChanges, issues, plan }`
Examples (inside OpenCode):
```json
{ "tool": "envsitter_copy", "args": { "from": ".env.production", "to": ".env.staging", "keys": ["API_URL"], "onConflict": "overwrite" } }
```
```json
{ "tool": "envsitter_copy", "args": { "from": ".env.production", "to": ".env.staging", "keys": ["API_URL"], "onConflict": "overwrite", "write": true } }
```
### `envsitter_format` / `envsitter_reorder`
Format/reorder an env file (no values in output).
- Input: `{ "filePath"?: string, "mode"?: "sections"|"global", "sort"?: "alpha"|"none", "write"?: boolean }`
- Output: JSON `{ file, mode, sort, willWrite, wrote, hasChanges, issues }`
Example (inside OpenCode):
```json
{ "tool": "envsitter_format", "args": { "filePath": ".env", "mode": "sections", "sort": "alpha", "write": true } }
```
### `envsitter_annotate`
Annotate an env key with a comment (no values in output).
- Input: `{ "filePath"?: string, "key": string, "comment": string, "line"?: number, "write"?: boolean }`
- Output: JSON `{ file, key, willWrite, wrote, hasChanges, issues, plan }`
Example (inside OpenCode):
```json
{ "tool": "envsitter_annotate", "args": { "filePath": ".env", "key": "DATABASE_URL", "comment": "prod only", "write": true } }
```
## Install & enable in OpenCode (alternatives)
### Option B: local plugin file (project-level)
If you want a local plugin file in-repo (or need local overrides), create `.opencode/plugin/envsitter-guard.ts`:
```ts
import EnvSitterGuard from "envsitter-guard";
export default EnvSitterGuard;
export { EnvSitterGuard } from "envsitter-guard";
```
Then create `.opencode/package.json` with the dependency so OpenCode can install it:
```json
{
"dependencies": {
"envsitter-guard": "latest"
}
}
```
Restart OpenCode; files in `.opencode/plugin/` are loaded automatically.
### Option C: global plugin file
Place a plugin file in `~/.config/opencode/plugin/` if you want it enabled for all projects.
(You can use the same contents as Option B.)
## Development
### Install
```bash
npm ci
```
### Typecheck
```bash
npm run typecheck
```
### Test
```bash
npm test
```
### Build (for publishing)
```bash
npm run build
```
## Notes
- This project intentionally avoids reading or printing `.env` values.
- EnvSitter CLI exists as an additional safe inspection option, for example: `npx envsitter keys --file .env`.
## License
MIT. See `LICENSE`.