@aliou/pi-guardrails 0.5.4 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -94
- package/commands/settings-command.ts +278 -0
- package/{pattern-editor.ts → components/pattern-editor.ts} +61 -10
- package/config.ts +185 -142
- package/hooks/index.ts +1 -7
- package/hooks/permission-gate.ts +247 -143
- package/hooks/protect-env-files.ts +122 -45
- package/index.ts +6 -3
- package/package.json +9 -3
- package/{events.ts → utils/events.ts} +1 -6
- package/utils/glob-expander.ts +128 -0
- package/utils/matching.ts +119 -0
- package/utils/migration.ts +135 -0
- package/utils/shell-utils.ts +139 -0
- package/array-editor.ts +0 -213
- package/config-schema.ts +0 -64
- package/hooks/enforce-package-manager.ts +0 -96
- package/hooks/prevent-brew.ts +0 -41
- package/hooks/prevent-python.ts +0 -45
- package/sectioned-settings.ts +0 -345
- package/settings-command.ts +0 -458
package/README.md
CHANGED
|
@@ -35,11 +35,12 @@ pi install npm:@aliou/pi-guardrails
|
|
|
35
35
|
|
|
36
36
|
## Features
|
|
37
37
|
|
|
38
|
-
- **prevent-brew**: Blocks Homebrew commands (disabled by default)
|
|
39
|
-
- **prevent-python**: Blocks Python/pip/poetry commands, suggests uv instead (disabled by default)
|
|
40
38
|
- **protect-env-files**: Prevents access to `.env` files (except `.example`/`.sample`/`.test`)
|
|
41
39
|
- **permission-gate**: Prompts for confirmation on dangerous commands
|
|
42
|
-
|
|
40
|
+
|
|
41
|
+
All hooks use structural shell parsing via `@aliou/sh` to avoid false positives from keywords inside commit messages, grep patterns, heredocs, or file paths. On parse failure, each hook falls back to regex matching (previous behavior).
|
|
42
|
+
|
|
43
|
+
> **Migration note**: The `preventBrew`, `preventPython`, `enforcePackageManager`, and `packageManager` fields have been removed from guardrails and moved to the [`@aliou/pi-toolchain`](../toolchain) extension. Old configs containing these fields are auto-cleaned on first load with a one-time warning. Install `@aliou/pi-toolchain` and configure `.pi/extensions/toolchain.json` instead.
|
|
43
44
|
|
|
44
45
|
## Configuration
|
|
45
46
|
|
|
@@ -54,28 +55,35 @@ Run `/guardrails:settings` to open an interactive settings UI with two tabs:
|
|
|
54
55
|
- **Local**: edit project-scoped config (`.pi/extensions/guardrails.json`)
|
|
55
56
|
- **Global**: edit global config (`~/.pi/agent/extensions/guardrails.json`)
|
|
56
57
|
|
|
57
|
-
Use `Tab` / `Shift+Tab` to switch tabs. Boolean settings can be toggled directly.
|
|
58
|
+
Use `Tab` / `Shift+Tab` to switch tabs. Boolean settings can be toggled directly.
|
|
59
|
+
|
|
60
|
+
### Migration from v0
|
|
61
|
+
|
|
62
|
+
Configs without a `version` field are automatically migrated on first load. The migration:
|
|
63
|
+
- Backs up the original as `guardrails.v0.json`
|
|
64
|
+
- Converts all string patterns to `{ pattern, regex: true }` to preserve behavior
|
|
65
|
+
- Adds a `version` field
|
|
58
66
|
|
|
59
67
|
### Configuration Schema
|
|
60
68
|
|
|
61
69
|
```json
|
|
62
70
|
{
|
|
71
|
+
"version": "0.7.0-20260204",
|
|
63
72
|
"enabled": true,
|
|
64
73
|
"features": {
|
|
65
|
-
"preventBrew": false,
|
|
66
|
-
"preventPython": false,
|
|
67
74
|
"protectEnvFiles": true,
|
|
68
|
-
"permissionGate": true
|
|
69
|
-
"enforcePackageManager": false
|
|
70
|
-
},
|
|
71
|
-
"packageManager": {
|
|
72
|
-
"selected": "npm"
|
|
75
|
+
"permissionGate": true
|
|
73
76
|
},
|
|
74
77
|
"envFiles": {
|
|
75
|
-
"protectedPatterns": [
|
|
78
|
+
"protectedPatterns": [
|
|
79
|
+
{ "pattern": ".env" },
|
|
80
|
+
{ "pattern": ".env.local" },
|
|
81
|
+
{ "pattern": ".env.production" }
|
|
82
|
+
],
|
|
76
83
|
"allowedPatterns": [
|
|
77
|
-
"
|
|
78
|
-
"
|
|
84
|
+
{ "pattern": ".env.example" },
|
|
85
|
+
{ "pattern": ".env.sample" },
|
|
86
|
+
{ "pattern": "*.example.env" }
|
|
79
87
|
],
|
|
80
88
|
"protectedDirectories": [],
|
|
81
89
|
"protectedTools": ["read", "write", "edit", "bash", "grep", "find", "ls"],
|
|
@@ -84,7 +92,8 @@ Use `Tab` / `Shift+Tab` to switch tabs. Boolean settings can be toggled directly
|
|
|
84
92
|
},
|
|
85
93
|
"permissionGate": {
|
|
86
94
|
"patterns": [
|
|
87
|
-
{ "pattern": "rm
|
|
95
|
+
{ "pattern": "rm -rf", "description": "recursive force delete" },
|
|
96
|
+
{ "pattern": "sudo", "description": "superuser command" }
|
|
88
97
|
],
|
|
89
98
|
"customPatterns": [],
|
|
90
99
|
"requireConfirmation": true,
|
|
@@ -96,31 +105,36 @@ Use `Tab` / `Shift+Tab` to switch tabs. Boolean settings can be toggled directly
|
|
|
96
105
|
|
|
97
106
|
All fields are optional. Missing fields use defaults shown above.
|
|
98
107
|
|
|
108
|
+
### Pattern Format
|
|
109
|
+
|
|
110
|
+
Patterns support two modes controlled by the `regex` flag:
|
|
111
|
+
|
|
112
|
+
**File patterns** (envFiles section):
|
|
113
|
+
- Default (`regex` omitted or `false`): glob matching against the filename. `*` matches any non-`/` chars, `?` matches a single char. Example: `.env.*` matches `.env.local`, `.env.production`.
|
|
114
|
+
- `regex: true`: full regex (case-insensitive) against the full path. Example: `{ "pattern": "\\.env$", "regex": true }`.
|
|
115
|
+
|
|
116
|
+
**Command patterns** (permissionGate section):
|
|
117
|
+
- Default (`regex` omitted or `false`): substring matching against the raw command string. Example: `"rm -rf"` matches any command containing `rm -rf`.
|
|
118
|
+
- `regex: true`: full regex against the raw command string. Example: `{ "pattern": "rm\\s+-rf", "regex": true }`.
|
|
119
|
+
|
|
120
|
+
Built-in dangerous command patterns (`rm -rf`, `sudo`, `dd if=`, `mkfs.*`, `chmod -R 777`, `chown -R`) are matched structurally via AST parsing, independent of the pattern format.
|
|
121
|
+
|
|
99
122
|
### Configuration Details
|
|
100
123
|
|
|
101
124
|
#### `features`
|
|
102
125
|
|
|
103
126
|
| Key | Default | Description |
|
|
104
127
|
|---|---|---|
|
|
105
|
-
| `preventBrew` | `false` | Block Homebrew install/upgrade commands |
|
|
106
|
-
| `preventPython` | `false` | Block python/pip/poetry commands (use uv instead) |
|
|
107
128
|
| `protectEnvFiles` | `true` | Block access to `.env` files containing secrets |
|
|
108
129
|
| `permissionGate` | `true` | Prompt for confirmation on dangerous commands |
|
|
109
|
-
| `enforcePackageManager` | `false` | Enforce a specific Node package manager |
|
|
110
|
-
|
|
111
|
-
#### `packageManager`
|
|
112
|
-
|
|
113
|
-
| Key | Default | Description |
|
|
114
|
-
|---|---|---|
|
|
115
|
-
| `selected` | `"npm"` | Package manager to enforce: `"npm"`, `"pnpm"`, or `"bun"` |
|
|
116
130
|
|
|
117
131
|
#### `envFiles`
|
|
118
132
|
|
|
119
133
|
| Key | Default | Description |
|
|
120
134
|
|---|---|---|
|
|
121
|
-
| `protectedPatterns` | `["
|
|
122
|
-
| `allowedPatterns` | `["
|
|
123
|
-
| `protectedDirectories` | `[]` |
|
|
135
|
+
| `protectedPatterns` | `[".env", ".env.local", ...]` | Patterns for files to protect (glob by default) |
|
|
136
|
+
| `allowedPatterns` | `[".env.example", "*.example.env", ...]` | Patterns for allowed exceptions |
|
|
137
|
+
| `protectedDirectories` | `[]` | Patterns for directories to protect |
|
|
124
138
|
| `protectedTools` | `["read", "write", "edit", "bash", "grep", "find", "ls"]` | Tools to intercept |
|
|
125
139
|
| `onlyBlockIfExists` | `true` | Only block if the file exists on disk |
|
|
126
140
|
| `blockMessage` | See defaults | Message shown when blocked. Supports `{file}` placeholder |
|
|
@@ -132,54 +146,47 @@ All fields are optional. Missing fields use defaults shown above.
|
|
|
132
146
|
| `patterns` | See defaults | Array of `{ pattern, description }` for dangerous commands |
|
|
133
147
|
| `customPatterns` | Not set | If set, replaces `patterns` entirely |
|
|
134
148
|
| `requireConfirmation` | `true` | Show confirmation dialog (if `false`, just warns) |
|
|
135
|
-
| `allowedPatterns` | `[]` |
|
|
136
|
-
| `autoDenyPatterns` | `[]` |
|
|
149
|
+
| `allowedPatterns` | `[]` | Patterns that bypass the gate |
|
|
150
|
+
| `autoDenyPatterns` | `[]` | Patterns that are blocked immediately without dialog |
|
|
137
151
|
|
|
138
152
|
### Examples
|
|
139
153
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
```json
|
|
143
|
-
{
|
|
144
|
-
"features": {
|
|
145
|
-
"preventBrew": true
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
Add a custom dangerous command pattern:
|
|
154
|
+
Add a custom dangerous command pattern (substring match):
|
|
151
155
|
|
|
152
156
|
```json
|
|
153
157
|
{
|
|
154
158
|
"permissionGate": {
|
|
155
159
|
"patterns": [
|
|
156
|
-
{ "pattern": "rm
|
|
157
|
-
{ "pattern": "
|
|
158
|
-
{ "pattern": "docker
|
|
160
|
+
{ "pattern": "rm -rf", "description": "recursive force delete" },
|
|
161
|
+
{ "pattern": "sudo", "description": "superuser command" },
|
|
162
|
+
{ "pattern": "docker system prune", "description": "docker system prune" }
|
|
159
163
|
]
|
|
160
164
|
}
|
|
161
165
|
}
|
|
162
166
|
```
|
|
163
167
|
|
|
164
|
-
|
|
168
|
+
Add a regex-based pattern:
|
|
165
169
|
|
|
166
170
|
```json
|
|
167
171
|
{
|
|
168
172
|
"permissionGate": {
|
|
169
|
-
"
|
|
173
|
+
"patterns": [
|
|
174
|
+
{ "pattern": "rm\\s+-rf\\s+/(?!tmp)", "description": "rm -rf outside /tmp", "regex": true }
|
|
175
|
+
]
|
|
170
176
|
}
|
|
171
177
|
}
|
|
172
178
|
```
|
|
173
179
|
|
|
174
|
-
|
|
180
|
+
Protect env files with glob patterns:
|
|
175
181
|
|
|
176
182
|
```json
|
|
177
183
|
{
|
|
178
|
-
"
|
|
179
|
-
"
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
184
|
+
"envFiles": {
|
|
185
|
+
"protectedPatterns": [
|
|
186
|
+
{ "pattern": ".env" },
|
|
187
|
+
{ "pattern": ".env.*" },
|
|
188
|
+
{ "pattern": ".dev.vars" }
|
|
189
|
+
]
|
|
183
190
|
}
|
|
184
191
|
}
|
|
185
192
|
```
|
|
@@ -194,7 +201,7 @@ Emitted when a tool call is blocked by any guardrail.
|
|
|
194
201
|
|
|
195
202
|
```typescript
|
|
196
203
|
interface GuardrailsBlockedEvent {
|
|
197
|
-
feature: "
|
|
204
|
+
feature: "protectEnvFiles" | "permissionGate";
|
|
198
205
|
toolName: string;
|
|
199
206
|
input: Record<string, unknown>;
|
|
200
207
|
reason: string;
|
|
@@ -218,37 +225,11 @@ The [presenter extension](../presenter) listens for `guardrails:dangerous` event
|
|
|
218
225
|
|
|
219
226
|
## Hooks
|
|
220
227
|
|
|
221
|
-
### prevent-brew
|
|
222
|
-
|
|
223
|
-
Blocks bash commands that attempt to install packages using Homebrew. Disabled by default. Enable via config if your project uses Nix.
|
|
224
|
-
|
|
225
|
-
Blocked patterns:
|
|
226
|
-
- `brew install`
|
|
227
|
-
- `brew cask install`
|
|
228
|
-
- `brew bundle`
|
|
229
|
-
- `brew upgrade`
|
|
230
|
-
- `brew reinstall`
|
|
231
|
-
|
|
232
|
-
### prevent-python
|
|
233
|
-
|
|
234
|
-
Blocks bash commands that use Python tooling directly. Disabled by default. Enable if your project uses uv for Python management.
|
|
235
|
-
|
|
236
|
-
Blocked patterns:
|
|
237
|
-
- `python`, `python3`
|
|
238
|
-
- `pip`, `pip3`
|
|
239
|
-
- `poetry`
|
|
240
|
-
- `pyenv`
|
|
241
|
-
- `virtualenv`, `venv`
|
|
242
|
-
|
|
243
228
|
### protect-env-files
|
|
244
229
|
|
|
245
|
-
Prevents accessing `.env` files that might contain secrets. Only allows access to safe variants
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
- `.env.test`
|
|
249
|
-
- `*.example.env`
|
|
250
|
-
- `*.sample.env`
|
|
251
|
-
- `*.test.env`
|
|
230
|
+
Prevents accessing `.env` files that might contain secrets. Only allows access to safe variants like `.env.example`, `.env.sample`, `.env.test`.
|
|
231
|
+
|
|
232
|
+
Shell globs (e.g. `.env*`) are expanded via `fd` to check if any expanded path matches a protected pattern.
|
|
252
233
|
|
|
253
234
|
Covers tools: `read`, `write`, `edit`, `bash`, `grep`, `find`, `ls` (configurable).
|
|
254
235
|
|
|
@@ -257,21 +238,9 @@ Covers tools: `read`, `write`, `edit`, `bash`, `grep`, `find`, `ls` (configurabl
|
|
|
257
238
|
Prompts user confirmation before executing dangerous commands:
|
|
258
239
|
- `rm -rf` (recursive force delete)
|
|
259
240
|
- `sudo` (superuser command)
|
|
260
|
-
- `: | sh` (piped shell execution)
|
|
261
241
|
- `dd if=` (disk write operation)
|
|
262
242
|
- `mkfs.` (filesystem format)
|
|
263
243
|
- `chmod -R 777` (insecure recursive permissions)
|
|
264
244
|
- `chown -R` (recursive ownership change)
|
|
265
245
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
### enforce-package-manager
|
|
269
|
-
|
|
270
|
-
Enforces using a specific Node package manager. Disabled by default. When enabled, blocks commands using non-selected package managers.
|
|
271
|
-
|
|
272
|
-
Configure via `packageManager.selected`:
|
|
273
|
-
- `"npm"` (default)
|
|
274
|
-
- `"pnpm"`
|
|
275
|
-
- `"bun"`
|
|
276
|
-
|
|
277
|
-
Example: If `selected` is `"pnpm"`, running `npm install` or `bun add` will be blocked with a message instructing the agent to use `pnpm` instead.
|
|
246
|
+
Built-in patterns are matched structurally (AST-based). Custom patterns use substring or regex matching. Supports allow-lists and auto-deny lists.
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ArrayEditor,
|
|
3
|
+
getNestedValue,
|
|
4
|
+
registerSettingsCommand,
|
|
5
|
+
type SettingsSection,
|
|
6
|
+
setNestedValue,
|
|
7
|
+
} from "@aliou/pi-utils-settings";
|
|
8
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
9
|
+
import { getSettingsListTheme } from "@mariozechner/pi-coding-agent";
|
|
10
|
+
import { PatternEditor } from "../components/pattern-editor";
|
|
11
|
+
import type {
|
|
12
|
+
DangerousPattern,
|
|
13
|
+
GuardrailsConfig,
|
|
14
|
+
PatternConfig,
|
|
15
|
+
ResolvedConfig,
|
|
16
|
+
} from "../config";
|
|
17
|
+
import { configLoader } from "../config";
|
|
18
|
+
|
|
19
|
+
type FeatureKey = keyof ResolvedConfig["features"];
|
|
20
|
+
|
|
21
|
+
const FEATURE_UI: Record<FeatureKey, { label: string; description: string }> = {
|
|
22
|
+
protectEnvFiles: {
|
|
23
|
+
label: "Protect .env files",
|
|
24
|
+
description: "Block access to .env files containing secrets",
|
|
25
|
+
},
|
|
26
|
+
permissionGate: {
|
|
27
|
+
label: "Permission gate",
|
|
28
|
+
description:
|
|
29
|
+
"Prompt for confirmation on dangerous commands (rm -rf, sudo, etc.)",
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export function registerGuardrailsSettings(pi: ExtensionAPI): void {
|
|
34
|
+
registerSettingsCommand<GuardrailsConfig, ResolvedConfig>(pi, {
|
|
35
|
+
commandName: "guardrails:settings",
|
|
36
|
+
title: "Guardrails Settings",
|
|
37
|
+
configStore: configLoader,
|
|
38
|
+
buildSections: (
|
|
39
|
+
tabConfig: GuardrailsConfig | null,
|
|
40
|
+
resolved: ResolvedConfig,
|
|
41
|
+
{ setDraft },
|
|
42
|
+
): SettingsSection[] => {
|
|
43
|
+
const settingsTheme = getSettingsListTheme();
|
|
44
|
+
// --- Helpers ---
|
|
45
|
+
|
|
46
|
+
function count(id: string): string {
|
|
47
|
+
const val =
|
|
48
|
+
(getNestedValue(tabConfig ?? {}, id) as unknown[] | undefined) ??
|
|
49
|
+
(getNestedValue(resolved, id) as unknown[]) ??
|
|
50
|
+
[];
|
|
51
|
+
return `${val.length} items`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function applyDraft(id: string, value: unknown): void {
|
|
55
|
+
const updated = structuredClone(tabConfig ?? {}) as GuardrailsConfig;
|
|
56
|
+
setNestedValue(updated, id, value);
|
|
57
|
+
setDraft(updated);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// --- Submenu factories ---
|
|
61
|
+
|
|
62
|
+
function stringArraySubmenu(id: string, label: string) {
|
|
63
|
+
return (_val: string, submenuDone: (v?: string) => void) => {
|
|
64
|
+
const items =
|
|
65
|
+
(getNestedValue(tabConfig ?? {}, id) as string[] | undefined) ??
|
|
66
|
+
(getNestedValue(resolved, id) as string[]) ??
|
|
67
|
+
[];
|
|
68
|
+
let latest = [...items];
|
|
69
|
+
return new ArrayEditor({
|
|
70
|
+
label,
|
|
71
|
+
items: [...items],
|
|
72
|
+
theme: settingsTheme,
|
|
73
|
+
onSave: (newItems) => {
|
|
74
|
+
latest = newItems;
|
|
75
|
+
applyDraft(id, newItems);
|
|
76
|
+
},
|
|
77
|
+
onDone: () => submenuDone(`${latest.length} items`),
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function patternSubmenu(
|
|
83
|
+
id: string,
|
|
84
|
+
label: string,
|
|
85
|
+
context?: "file" | "command",
|
|
86
|
+
) {
|
|
87
|
+
return (_val: string, submenuDone: (v?: string) => void) => {
|
|
88
|
+
const items =
|
|
89
|
+
(getNestedValue(tabConfig ?? {}, id) as
|
|
90
|
+
| DangerousPattern[]
|
|
91
|
+
| undefined) ??
|
|
92
|
+
(getNestedValue(resolved, id) as DangerousPattern[]) ??
|
|
93
|
+
[];
|
|
94
|
+
let latestCount = items.length;
|
|
95
|
+
return new PatternEditor({
|
|
96
|
+
label,
|
|
97
|
+
items: [...items],
|
|
98
|
+
theme: settingsTheme,
|
|
99
|
+
context,
|
|
100
|
+
onSave: (newItems) => {
|
|
101
|
+
latestCount = newItems.length;
|
|
102
|
+
applyDraft(id, newItems);
|
|
103
|
+
},
|
|
104
|
+
onDone: () => submenuDone(`${latestCount} items`),
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function patternConfigSubmenu(
|
|
110
|
+
id: string,
|
|
111
|
+
label: string,
|
|
112
|
+
context?: "file" | "command",
|
|
113
|
+
) {
|
|
114
|
+
return (_val: string, submenuDone: (v?: string) => void) => {
|
|
115
|
+
const currentItems =
|
|
116
|
+
(getNestedValue(tabConfig ?? {}, id) as
|
|
117
|
+
| PatternConfig[]
|
|
118
|
+
| undefined) ??
|
|
119
|
+
(getNestedValue(resolved, id) as PatternConfig[]) ??
|
|
120
|
+
[];
|
|
121
|
+
const items = currentItems.map((p) => ({
|
|
122
|
+
pattern: p.pattern,
|
|
123
|
+
description: p.pattern,
|
|
124
|
+
regex: p.regex,
|
|
125
|
+
}));
|
|
126
|
+
let latestCount = items.length;
|
|
127
|
+
return new PatternEditor({
|
|
128
|
+
label,
|
|
129
|
+
items,
|
|
130
|
+
theme: settingsTheme,
|
|
131
|
+
context,
|
|
132
|
+
onSave: (newItems) => {
|
|
133
|
+
latestCount = newItems.length;
|
|
134
|
+
const configs: PatternConfig[] = newItems.map((p) => {
|
|
135
|
+
const cfg: PatternConfig = { pattern: p.pattern };
|
|
136
|
+
if (p.regex) cfg.regex = true;
|
|
137
|
+
return cfg;
|
|
138
|
+
});
|
|
139
|
+
applyDraft(id, configs);
|
|
140
|
+
},
|
|
141
|
+
onDone: () => submenuDone(`${latestCount} items`),
|
|
142
|
+
});
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// --- Sections ---
|
|
147
|
+
|
|
148
|
+
const featureItems = (Object.keys(FEATURE_UI) as FeatureKey[]).map(
|
|
149
|
+
(key) => ({
|
|
150
|
+
id: `features.${key}`,
|
|
151
|
+
label: FEATURE_UI[key].label,
|
|
152
|
+
description: FEATURE_UI[key].description,
|
|
153
|
+
currentValue:
|
|
154
|
+
(tabConfig?.features?.[key] ?? resolved.features[key])
|
|
155
|
+
? "enabled"
|
|
156
|
+
: "disabled",
|
|
157
|
+
values: ["enabled", "disabled"],
|
|
158
|
+
}),
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
return [
|
|
162
|
+
{ label: "Features", items: featureItems },
|
|
163
|
+
{
|
|
164
|
+
label: "Env Files",
|
|
165
|
+
items: [
|
|
166
|
+
{
|
|
167
|
+
id: "envFiles.onlyBlockIfExists",
|
|
168
|
+
label: "Only block existing files",
|
|
169
|
+
description:
|
|
170
|
+
"Only block .env file access if the file exists on disk",
|
|
171
|
+
currentValue:
|
|
172
|
+
(tabConfig?.envFiles?.onlyBlockIfExists ??
|
|
173
|
+
resolved.envFiles.onlyBlockIfExists)
|
|
174
|
+
? "on"
|
|
175
|
+
: "off",
|
|
176
|
+
values: ["on", "off"],
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
id: "envFiles.protectedPatterns",
|
|
180
|
+
label: "Protected patterns",
|
|
181
|
+
description: "Patterns for files to protect (e.g. .env.local)",
|
|
182
|
+
currentValue: count("envFiles.protectedPatterns"),
|
|
183
|
+
submenu: patternConfigSubmenu(
|
|
184
|
+
"envFiles.protectedPatterns",
|
|
185
|
+
"Protected Patterns",
|
|
186
|
+
"file",
|
|
187
|
+
),
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
id: "envFiles.allowedPatterns",
|
|
191
|
+
label: "Allowed patterns",
|
|
192
|
+
description: "Patterns for exceptions (e.g. .env.example)",
|
|
193
|
+
currentValue: count("envFiles.allowedPatterns"),
|
|
194
|
+
submenu: patternConfigSubmenu(
|
|
195
|
+
"envFiles.allowedPatterns",
|
|
196
|
+
"Allowed Patterns",
|
|
197
|
+
"file",
|
|
198
|
+
),
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
id: "envFiles.protectedDirectories",
|
|
202
|
+
label: "Protected directories",
|
|
203
|
+
description: "Patterns for directories to protect",
|
|
204
|
+
currentValue: count("envFiles.protectedDirectories"),
|
|
205
|
+
submenu: patternConfigSubmenu(
|
|
206
|
+
"envFiles.protectedDirectories",
|
|
207
|
+
"Protected Directories",
|
|
208
|
+
"file",
|
|
209
|
+
),
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
id: "envFiles.protectedTools",
|
|
213
|
+
label: "Protected tools",
|
|
214
|
+
description:
|
|
215
|
+
"Tools to intercept (read, write, edit, bash, grep, find, ls)",
|
|
216
|
+
currentValue: count("envFiles.protectedTools"),
|
|
217
|
+
submenu: stringArraySubmenu(
|
|
218
|
+
"envFiles.protectedTools",
|
|
219
|
+
"Protected Tools",
|
|
220
|
+
),
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
label: "Permission Gate",
|
|
226
|
+
items: [
|
|
227
|
+
{
|
|
228
|
+
id: "permissionGate.requireConfirmation",
|
|
229
|
+
label: "Require confirmation",
|
|
230
|
+
description:
|
|
231
|
+
"Show confirmation dialog for dangerous commands (if off, just warns)",
|
|
232
|
+
currentValue:
|
|
233
|
+
(tabConfig?.permissionGate?.requireConfirmation ??
|
|
234
|
+
resolved.permissionGate.requireConfirmation)
|
|
235
|
+
? "on"
|
|
236
|
+
: "off",
|
|
237
|
+
values: ["on", "off"],
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
id: "permissionGate.patterns",
|
|
241
|
+
label: "Dangerous patterns",
|
|
242
|
+
description: "Command patterns that trigger the permission gate",
|
|
243
|
+
currentValue: count("permissionGate.patterns"),
|
|
244
|
+
submenu: patternSubmenu(
|
|
245
|
+
"permissionGate.patterns",
|
|
246
|
+
"Dangerous Patterns",
|
|
247
|
+
"command",
|
|
248
|
+
),
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
id: "permissionGate.allowedPatterns",
|
|
252
|
+
label: "Allowed commands",
|
|
253
|
+
description: "Patterns that bypass the permission gate entirely",
|
|
254
|
+
currentValue: count("permissionGate.allowedPatterns"),
|
|
255
|
+
submenu: patternConfigSubmenu(
|
|
256
|
+
"permissionGate.allowedPatterns",
|
|
257
|
+
"Allowed Commands",
|
|
258
|
+
"command",
|
|
259
|
+
),
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
id: "permissionGate.autoDenyPatterns",
|
|
263
|
+
label: "Auto-deny patterns",
|
|
264
|
+
description:
|
|
265
|
+
"Patterns that block commands immediately without dialog",
|
|
266
|
+
currentValue: count("permissionGate.autoDenyPatterns"),
|
|
267
|
+
submenu: patternConfigSubmenu(
|
|
268
|
+
"permissionGate.autoDenyPatterns",
|
|
269
|
+
"Auto-Deny Patterns",
|
|
270
|
+
"command",
|
|
271
|
+
),
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
},
|
|
275
|
+
];
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
}
|