permission-pi 1.0.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 +277 -0
- package/package.json +29 -0
- package/permission-core.ts +1194 -0
- package/permission.ts +609 -0
- package/tests/permission.test.ts +1438 -0
package/README.md
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# Permission Extension
|
|
2
|
+
|
|
3
|
+
Layered permission control for pi-coding-agent.
|
|
4
|
+
|
|
5
|
+
## Levels
|
|
6
|
+
|
|
7
|
+
| Level | Description | Allowed Operations |
|
|
8
|
+
|-------|-------------|-------------------|
|
|
9
|
+
| **minimal** | Read-only (default) | `cat`, `ls`, `grep`, `git status/log/diff`, `npm list` |
|
|
10
|
+
| **low** | File operations | + `write`/`edit` files |
|
|
11
|
+
| **medium** | Dev operations | + `npm install`, `git commit`, build commands |
|
|
12
|
+
| **high** | Full operations | + `git push`, deployments, scripts |
|
|
13
|
+
|
|
14
|
+
**Dangerous commands** (always prompt, even at high): `sudo`, `rm -rf`, `chmod 777`, `dd`, `mkfs`
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
### Interactive Mode
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Extension loads automatically from ~/.pi/agent/extensions/ or .pi/extensions/
|
|
22
|
+
pi
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Commands:**
|
|
26
|
+
- `/permission` - Show selector to change level
|
|
27
|
+
- `/permission medium` - Set level directly (asks session/global)
|
|
28
|
+
- `/permission-mode` - Switch between ask/block when permission is required
|
|
29
|
+
- `/permission-mode block` - Block instead of prompting
|
|
30
|
+
|
|
31
|
+
**When a command needs higher permission:**
|
|
32
|
+
```
|
|
33
|
+
🔒 Requires Medium: npm install lodash
|
|
34
|
+
|
|
35
|
+
[Allow once] → Execute this command only
|
|
36
|
+
[Allow all (Medium)] → Update global settings and execute
|
|
37
|
+
[Cancel] → Don't execute
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
If permission mode is set to block, commands that require higher permission are blocked without prompting. Use `/permission-mode ask` to restore prompts.
|
|
41
|
+
|
|
42
|
+
### Print Mode
|
|
43
|
+
|
|
44
|
+
Permission mode is ignored in print mode; insufficient permissions always block.
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Set level via environment variable
|
|
48
|
+
PI_PERMISSION_LEVEL=medium pi -p "install deps and run tests"
|
|
49
|
+
|
|
50
|
+
# Bypass all permission checks (CI/containers - dangerous!)
|
|
51
|
+
PI_PERMISSION_LEVEL=bypassed pi -p "do anything"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**If permission is insufficient:**
|
|
55
|
+
The command is blocked but execution continues. The agent receives:
|
|
56
|
+
```
|
|
57
|
+
Blocked by permission (minimal). Command: npm install lodash
|
|
58
|
+
Allowed at this level: read-only (cat, ls, grep, git status/diff/log, npm list, version checks)
|
|
59
|
+
User can re-run with: PI_PERMISSION_LEVEL=medium pi -p "..."
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The agent can then work around the limitation or inform the user.
|
|
63
|
+
|
|
64
|
+
## Environment Variables
|
|
65
|
+
|
|
66
|
+
| Variable | Values | Description |
|
|
67
|
+
|----------|--------|-------------|
|
|
68
|
+
| `PI_PERMISSION_LEVEL` | `minimal`, `low`, `medium`, `high`, `bypassed` | Set permission level |
|
|
69
|
+
|
|
70
|
+
## Settings
|
|
71
|
+
|
|
72
|
+
Global settings stored in `~/.pi/agent/settings.json`:
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"permissionLevel": "medium",
|
|
77
|
+
"permissionMode": "ask"
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`permissionMode` accepts `ask` (prompt) or `block` (deny without prompting).
|
|
82
|
+
|
|
83
|
+
## Custom Configuration
|
|
84
|
+
|
|
85
|
+
Configure permission overrides and prefix mappings in `~/.pi/agent/settings.json`:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"permissionLevel": "medium",
|
|
90
|
+
"permissionMode": "ask",
|
|
91
|
+
"permissionConfig": {
|
|
92
|
+
"overrides": {
|
|
93
|
+
"minimal": ["tmux list-*", "tmux show-*"],
|
|
94
|
+
"medium": ["tmux attach*", "tmux new*"],
|
|
95
|
+
"high": ["rm -rf *"],
|
|
96
|
+
"dangerous": ["dd if=* of=/dev/*"]
|
|
97
|
+
},
|
|
98
|
+
"prefixMappings": [
|
|
99
|
+
{ "from": "fvm flutter", "to": "flutter" },
|
|
100
|
+
{ "from": "nvm exec", "to": "" },
|
|
101
|
+
{ "from": "rbenv exec", "to": "" }
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Override Patterns
|
|
108
|
+
|
|
109
|
+
Glob patterns matched against the full command:
|
|
110
|
+
- `*` matches any characters
|
|
111
|
+
- `?` matches single character
|
|
112
|
+
- Patterns are case-insensitive
|
|
113
|
+
|
|
114
|
+
Override priority (highest to lowest):
|
|
115
|
+
1. `dangerous` - Always prompt, even at high level
|
|
116
|
+
2. `high` - Require high permission
|
|
117
|
+
3. `medium` - Require medium permission
|
|
118
|
+
4. `low` - Require low permission
|
|
119
|
+
5. `minimal` - Allow at minimal (read-only)
|
|
120
|
+
|
|
121
|
+
> **Note:** When a command matches patterns in multiple levels, the **most restrictive** level wins. Avoid overlapping patterns across levels. For example, don't put `tmux *` in medium if you want `tmux list-*` to be minimal.
|
|
122
|
+
|
|
123
|
+
**Examples:**
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"overrides": {
|
|
127
|
+
"minimal": [
|
|
128
|
+
"tmux list-*", // tmux list-sessions, tmux list-windows, etc.
|
|
129
|
+
"tmux show-*", // tmux show-options, tmux show-messages, etc.
|
|
130
|
+
"screen -list" // List screen sessions
|
|
131
|
+
],
|
|
132
|
+
"medium": [
|
|
133
|
+
"tmux attach*", // Attach to sessions
|
|
134
|
+
"tmux new*", // Create new sessions
|
|
135
|
+
"screen -r *" // Reattach to screen
|
|
136
|
+
],
|
|
137
|
+
"high": [
|
|
138
|
+
"rm -rf *", // Force rm with any arguments
|
|
139
|
+
"dd of=/dev/*" // dd writing to any device
|
|
140
|
+
],
|
|
141
|
+
"dangerous": [
|
|
142
|
+
"dd if=* of=/dev/*" // dd writing to device from any source
|
|
143
|
+
]
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Prefix Mappings
|
|
149
|
+
|
|
150
|
+
Normalize version manager commands to their base tools:
|
|
151
|
+
- `fvm flutter build` → treated as `flutter build` (medium)
|
|
152
|
+
- `rbenv exec ruby` → treated as `ruby` (classified normally)
|
|
153
|
+
|
|
154
|
+
**Common mappings:**
|
|
155
|
+
```json
|
|
156
|
+
{
|
|
157
|
+
"prefixMappings": [
|
|
158
|
+
{ "from": "fvm flutter", "to": "flutter" },
|
|
159
|
+
{ "from": "nvm exec", "to": "" },
|
|
160
|
+
{ "from": "rbenv exec", "to": "" },
|
|
161
|
+
{ "from": "pyenv exec", "to": "" }
|
|
162
|
+
]
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**How it works:**
|
|
167
|
+
1. Commands are checked against prefix mappings first
|
|
168
|
+
2. If a prefix matches, it's replaced with the mapped value
|
|
169
|
+
3. The normalized command is then classified
|
|
170
|
+
|
|
171
|
+
### /permission config Command
|
|
172
|
+
|
|
173
|
+
View and manage configuration from the CLI:
|
|
174
|
+
|
|
175
|
+
```
|
|
176
|
+
/permission config show # Display current configuration
|
|
177
|
+
/permission config reset # Reset to default (empty)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Edit `~/.pi/agent/settings.json` directly for full control.
|
|
181
|
+
|
|
182
|
+
## Command Classification
|
|
183
|
+
|
|
184
|
+
The principle: **building/installing is MEDIUM, running code is HIGH**.
|
|
185
|
+
|
|
186
|
+
### Minimal Level (Read-only)
|
|
187
|
+
- File reading: `cat`, `less`, `head`, `tail`, `bat`
|
|
188
|
+
- Directory: `ls`, `tree`, `pwd`, `find`, `fd`
|
|
189
|
+
- Search: `grep`, `rg`, `ag`
|
|
190
|
+
- Info: `echo`, `whoami`, `date`, `uname`, `ps`, `env`
|
|
191
|
+
- Git read: `git status`, `git log`, `git diff`, `git show`, `git branch`, `git fetch`
|
|
192
|
+
- Package info: `npm list`, `pip list`, `cargo tree`
|
|
193
|
+
|
|
194
|
+
### Medium Level (Build/Install/Test - Reversible)
|
|
195
|
+
- **Node.js**: `npm install/ci/test/build`, `yarn install/add/build/test`, `pnpm`, `bun`
|
|
196
|
+
- **npm run** (safe scripts only): `build`, `test`, `lint`, `format`, `check`, `typecheck`
|
|
197
|
+
- **Python**: `pip install`, `poetry install/build`, `pytest`
|
|
198
|
+
- **Rust**: `cargo build/test/check/clippy/fmt` (NOT `cargo run`)
|
|
199
|
+
- **Go**: `go build/test/get/mod` (NOT `go run`)
|
|
200
|
+
- **Ruby**: `gem install`, `bundle install`
|
|
201
|
+
- **CocoaPods**: `pod install`, `pod update`, `pod repo update`
|
|
202
|
+
- **PHP**: `composer install`
|
|
203
|
+
- **Java**: `mvn compile/test`, `gradle build/test`
|
|
204
|
+
- **.NET**: `dotnet build/test`
|
|
205
|
+
- **Git local**: `git add`, `git commit`, `git pull`, `git checkout`, `git merge`, `git clone`
|
|
206
|
+
- **Build tools**: `make`, `cmake`, `ninja`
|
|
207
|
+
- **Linters**: All static analysis tools that only check/report without executing code
|
|
208
|
+
- **JavaScript/TypeScript**: `eslint`, `prettier`, `tsc --noEmit`, `tslint`, `standard`, `xo`
|
|
209
|
+
- **Python**: `pylint`, `flake8`, `black`, `mypy`, `pyright`, `ruff`, `pyflakes`, `bandit`
|
|
210
|
+
- **Rust**: `cargo clippy`, `cargo fmt`, `rustfmt`
|
|
211
|
+
- **Go**: `gofmt`, `go vet`, `golangci-lint`, `golint`, `staticcheck`, `errcheck`, `misspell`
|
|
212
|
+
- **Ruby**: `rubocop`, `standardrb`, `reek`, `brakeman`
|
|
213
|
+
- **Swift**: `swiftlint`, `swiftformat`
|
|
214
|
+
- **Kotlin**: `ktlint`, `detekt`
|
|
215
|
+
- **Dart/Flutter**: `dart analyze`, `flutter analyze`, `dart format`, `flutter format`
|
|
216
|
+
- **C/C++**: `clang-tidy`, `clang-format`, `cppcheck`
|
|
217
|
+
- **Java**: `checkstyle`, `pmd`, `spotbugs`, `error-prone`
|
|
218
|
+
- **C#**: `dotnet format`, `dotnet build -t:RunCodeAnalysis`
|
|
219
|
+
- **PHP**: `phpcs`, `phpmd`, `phpstan`, `psalm`, `php-cs-fixer`
|
|
220
|
+
- **Lua**: `luacheck`
|
|
221
|
+
- **Shell**: `shellcheck`
|
|
222
|
+
- **Infrastructure as Code**: `checkov`, `tflint`, `terraform validate`
|
|
223
|
+
- **Protocol Buffers**: `buf lint`, `protoc --lint`
|
|
224
|
+
- **SQL**: `sqlfluff`
|
|
225
|
+
- **YAML**: `yamllint`
|
|
226
|
+
- **Markdown**: `markdownlint`
|
|
227
|
+
- **HTML/Django**: `djlint`, `djhtml`
|
|
228
|
+
- **Git**: `commitlint`
|
|
229
|
+
- **File ops**: `mkdir`, `touch`, `cp`, `mv`
|
|
230
|
+
|
|
231
|
+
### High Level (Runs Code / Irreversible)
|
|
232
|
+
- **Running code**: `python script.py`, `node app.js`, `cargo run`, `go run`
|
|
233
|
+
- **npm run** (unsafe scripts): `dev`, `start`, `serve`, `watch`, `preview`
|
|
234
|
+
- **Package executors**: `npx`, `bunx`, `pnpx` (run arbitrary packages)
|
|
235
|
+
- **Git remote**: `git push`, `git push --force`
|
|
236
|
+
- **Git irreversible**: `git reset --hard`, `git clean`, `git restore`
|
|
237
|
+
- **Network**: `curl`, `wget` (can't verify trusted endpoints)
|
|
238
|
+
- **Deployment**: `docker push`, `kubectl`, `helm`, `terraform`
|
|
239
|
+
- **Remote access**: `ssh`, `scp`, `rsync`
|
|
240
|
+
- **Shell execution**: `eval`, `exec`, `source`, `xargs`
|
|
241
|
+
|
|
242
|
+
### Dangerous (Always Prompt)
|
|
243
|
+
- `sudo` (any form)
|
|
244
|
+
- `rm` with `-r` AND `-f` flags
|
|
245
|
+
- `chmod 777` or `a+rwx`
|
|
246
|
+
- `dd of=/dev/...`
|
|
247
|
+
- `mkfs`, `mkfs.ext4`, `fdisk`, `parted`
|
|
248
|
+
- `shutdown`, `reboot`, `halt`, `poweroff`
|
|
249
|
+
|
|
250
|
+
## Shell Trick Detection
|
|
251
|
+
|
|
252
|
+
Commands containing these patterns require HIGH permission:
|
|
253
|
+
- Command substitution: `$(cmd)`, `` `cmd` ``
|
|
254
|
+
- Process substitution: `<(cmd)`, `>(cmd)`
|
|
255
|
+
- Dangerous expansions: `${VAR:-$(cmd)}` (nested command substitution)
|
|
256
|
+
|
|
257
|
+
## Installation
|
|
258
|
+
|
|
259
|
+
Install the package and enable extensions:
|
|
260
|
+
```bash
|
|
261
|
+
pi install git:github.com/prateekmedia/pi-hooks
|
|
262
|
+
pi config
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Dependencies are installed automatically during `pi install`.
|
|
266
|
+
|
|
267
|
+
## File Structure
|
|
268
|
+
|
|
269
|
+
| File | Purpose |
|
|
270
|
+
|------|---------|
|
|
271
|
+
| `permission.ts` | Extension (entry point + state management + handlers) |
|
|
272
|
+
| `permission-core.ts` | Core permission logic (classification, config) |
|
|
273
|
+
| `package.json` | Declares extension via "pi" field |
|
|
274
|
+
|
|
275
|
+
## License
|
|
276
|
+
|
|
277
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "permission-pi",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Layered permission control extension for pi-coding-agent",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "npx tsx tests/permission.test.ts"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [
|
|
10
|
+
"permission",
|
|
11
|
+
"security",
|
|
12
|
+
"pi-coding-agent",
|
|
13
|
+
"extension",
|
|
14
|
+
"pi-package"
|
|
15
|
+
],
|
|
16
|
+
"author": "",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"pi": {
|
|
19
|
+
"extensions": ["./permission.ts"]
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"shell-quote": "^1.8.3"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@mariozechner/pi-coding-agent": "^0.50.0",
|
|
26
|
+
"@types/node": "^25.0.3",
|
|
27
|
+
"typescript": "^5.9.3"
|
|
28
|
+
}
|
|
29
|
+
}
|