auto-permission-system 3.9.0 → 3.12.0

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/CHANGELOG.md CHANGED
@@ -3,15 +3,6 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  Entries below [1.0.0] are generated automatically by [semantic-release](https://semantic-release.gitbook.io/) from [Conventional Commits](https://www.conventionalcommits.org/) — do not edit by hand.
5
5
 
6
- # [3.9.0](https://github.com/Navin009/auto-permission-system/compare/v3.8.0...v3.9.0) (2026-10-13)
7
-
8
- ### Features
9
-
10
- * **demo:** crisp widescreen HD terminal recording (`demo.mp4`, `demo.gif`) demonstrating Cloud Auto Mode and Advanced Secure Mode
11
- * **branding:** official security shield logo and banner in documentation and package
12
- * **docs:** comprehensive FAQ, Cloud Auto Mode architecture, interactive prompt guides, and zero-trust documentation
13
- * **metadata:** package keywords, clean npm manifest, and remove legacy PyPI references
14
-
15
6
  # [2.0.0](https://github.com/Navin009/auto-permission-system/compare/v1.2.0...v2.0.0) (2026-10-02)
16
7
 
17
8
 
package/README.md CHANGED
@@ -11,11 +11,11 @@
11
11
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
12
12
  [![Pi package](https://img.shields.io/badge/pi--package-gallery-blueviolet)](https://pi.dev/packages)
13
13
  [![CI](https://github.com/Navin009/auto-permission-system/actions/workflows/ci.yml/badge.svg)](https://github.com/Navin009/auto-permission-system/actions/workflows/ci.yml)
14
- [![Cloud Auto Mode for Pi](https://img.shields.io/badge/Pi-Cloud_Auto_Mode-blue.svg)](#secure-auto-mode--cloud-auto-mode)
14
+ [![Claude Auto Mode for Pi](https://img.shields.io/badge/Pi-Claude_Auto_Mode-blue.svg)](#secure-auto-mode--claude-auto-mode)
15
15
  [![Security for Pi](https://img.shields.io/badge/Security-for_Pi-green.svg)](#security-for-pi-how-it-works)
16
16
  [![Permission System for Pi](https://img.shields.io/badge/Permission_System-for_Pi-orange.svg)](#permission-system--modes)
17
17
 
18
- **Auto Permission System** is a zero-trust permission architecture and **Secure Auto Mode / Cloud Auto Mode for the [Pi coding agent](https://pi.dev)**. It brings the confidence of **Claude's auto mode** to Pi—enabling autonomous agent workflows while actively scanning files, shell commands, and tool content to **prevent data breaches and credential exfiltration**.
18
+ **Auto Permission System** is a zero-trust permission architecture and **Secure Auto Mode / Claude Auto Mode for the [Pi coding agent](https://pi.dev)**. It brings the confidence of **Claude's auto mode** to Pi—enabling autonomous agent workflows while actively scanning files, shell commands, and tool content to **prevent data breaches and credential exfiltration**.
19
19
 
20
20
  Whether running interactively with keyboard-driven ask prompts or running headlessly in CI/CD pipelines, this package enforces multi-layer boundaries across filesystem access, network egress, shell commands, and MCP tools.
21
21
 
@@ -25,14 +25,12 @@ Whether running interactively with keyboard-driven ask prompts or running headle
25
25
 
26
26
  ![Auto Permission System Demo](https://raw.githubusercontent.com/Navin009/auto-permission-system/main/assets/demo.gif)
27
27
 
28
- > **High-Definition Video:** Watch or download the full-resolution recording at [assets/demo.mp4](https://raw.githubusercontent.com/Navin009/auto-permission-system/main/assets/demo.mp4) (H.264, 60fps). Run `./demo/run-demo.sh` locally to reproduce this live simulation.
29
-
30
28
  ### Demonstrated Moments in the Recording:
31
29
  1. **Interactive `.env` Read Guard:** Intercepts `read(".env")` before the AI can see secrets. Displays a 10s countdown prompt defaulting to `No` (Block).
32
30
  2. **Kernel OS Sandbox Confinement:** Prevents bash commands from writing to system paths outside allowed roots (`EROFS: Read-only file system`).
33
31
  3. **Governed Network Egress:** Catches unknown outbound connections (`curl`) with an interactive domain ask prompt and a 15s countdown timer.
34
32
  4. **Real-Time API Key & Secret Redaction:** Automatically detects exposed API tokens (`sk-proj-...`, `ghp_...`) in command output and replaces them with `***REDACTED***` before reaching the LLM context.
35
- 5. **Autonomous Cloud Auto Mode:** Demonstrates headless, fail-closed CI execution (`pi -p`) with zero deadlocks and zero hangs.
33
+ 5. **Autonomous Claude Auto Mode:** Demonstrates headless, fail-closed CI execution (`pi -p`) with zero deadlocks and zero hangs.
36
34
 
37
35
  ---
38
36
 
@@ -43,7 +41,7 @@ Whether running interactively with keyboard-driven ask prompts or running headle
43
41
  - [Key Features at a Glance](#key-features-at-a-glance)
44
42
  - [Security for Pi: How It Works](#security-for-pi-how-it-works)
45
43
  - [Interactive Ask Prompts](#interactive-ask-prompts)
46
- - [Secure Auto Mode & Cloud Auto Mode](#secure-auto-mode--cloud-auto-mode)
44
+ - [Secure Auto Mode & Claude Auto Mode](#secure-auto-mode--claude-auto-mode)
47
45
  - [Real-Time Secret Detection & Content Scanning](#real-time-secret-detection--content-scanning)
48
46
  - [Permission System & Modes](#permission-system--modes)
49
47
  - [Configuration Reference](#configuration-reference)
@@ -100,7 +98,7 @@ Once installed, the extension automatically loads whenever you launch `pi`. Chec
100
98
  | Feature | Description | Protection Level |
101
99
  |---|---|---|
102
100
  | 🛡️ **3-Layer Defense** | OS Kernel sandbox (`bubblewrap`/`sandbox-exec`) + in-process tool guard + real-time content redactor. | Zero-Trust |
103
- | ⚡ **Secure Auto Mode** | Autonomous execution inspired by Claude's auto mode; scans files and output to prevent data breaches. | Autonomous / Cloud |
101
+ | ⚡ **Secure Auto Mode** | Autonomous execution inspired by Claude's auto mode; scans files and output to prevent data breaches. | Autonomous / Claude |
104
102
  | 🔑 **Interactive Ask Prompts** | Real-time terminal prompts with 10s countdowns (`Once`, `Session`, `Project`, `Global`). | Interactive Human-in-the-Loop |
105
103
  | 🔍 **AST & Regex Secret Scanner** | Scans tool stdout/stderr for API keys, private keys, and tokens, redacting them before model context. | Data Breach Prevention |
106
104
  | 🌐 **Network Egress Gate** | Intercepts unapproved network connections and prompts for confirmation with wildcards. | Data Leak Prevention |
@@ -162,7 +160,7 @@ When an unlisted domain, `.env` file, or directory write is requested during an
162
160
 
163
161
  > No
164
162
  Yes, just this once
165
- Yes, for this session
163
+ Yes, for this conversation
166
164
 
167
165
  Default: No (10s)
168
166
  ```
@@ -179,7 +177,7 @@ When an unlisted domain, `.env` file, or directory write is requested during an
179
177
 
180
178
  > No
181
179
  Yes, just this once
182
- Yes, all in group for this session
180
+ Yes, all in group for this conversation
183
181
  Yes, always…
184
182
 
185
183
  Default: Yes, just this once (15s)
@@ -191,33 +189,34 @@ When an unlisted domain, `.env` file, or directory write is requested during an
191
189
  ⚠ Sensitive information detected: API Secret Key
192
190
 
193
191
  Tool bash (node verify_service.js)
194
- Finding sk-(?!ant-)[A-Za-z0-9_-] (score: 90 / CRITICAL)
192
+ Finding 23: sk-(?!ant-)[A-Za-z0-9_-]
195
193
  Question Should the AI be allowed to see it?
196
194
 
197
195
  > No, keep private (recommended)
198
196
  Yes, allow
199
197
  ```
198
+ - `Finding` prints the **complete** detected line and its line number — one `Finding` row per match when several secrets appear, with no truncation.
200
199
  - Choosing **No, keep private** replaces the token with `***REDACTED***` in the model's context window, preventing credentials from leaking to LLM providers.
201
200
 
202
201
  ---
203
202
 
204
- ## Secure Auto Mode & Cloud Auto Mode
203
+ ## Secure Auto Mode & Claude Auto Mode
205
204
 
206
205
  In modern AI coding tools like Claude Code, developers love **auto mode** because the agent can execute tasks autonomously without constantly prompting for confirmation on trivial, harmless steps.
207
206
 
208
207
  However, unconstrained auto mode is a significant data breach risk. If an autonomous agent reads credentials, runs unvetted scripts, or sends code to third-party endpoints, the entire developer environment can be compromised.
209
208
 
210
- **Cloud Auto Mode for Pi** delivers **Claude-style auto mode with zero-trust security**:
209
+ **Claude Auto Mode for Pi** delivers **Claude-style auto mode with zero-trust security**:
211
210
  - **Autonomous Execution with Real-Time Guardrails:** The agent operates autonomously within approved boundaries, allowing rapid development while guarding sensitive resources.
212
211
  - **Advanced Secure Scanning:** Uses `advanced-secure` mode to scan files, commands, and content to stop data breaches before they can occur.
213
212
  - **Fail-Closed in Headless CI/CD:** When running unattended (`pi -p`, GitHub Actions, Docker, remote devboxes), unapproved operations fail closed immediately with a structured reason instead of hanging or deadlocking the pipeline.
214
213
 
215
- ### Activating Cloud Auto Mode in CI/CD:
214
+ ### Activating Claude Auto Mode in CI/CD:
216
215
 
217
216
  ```bash
218
- # 1. Copy the shipped Cloud Auto Mode template to your repository:
217
+ # 1. Copy the shipped Claude Auto Mode template to your repository:
219
218
  mkdir -p .pi
220
- cp node_modules/auto-permission-system/sandbox.cloud.json .pi/sandbox.json
219
+ cp node_modules/auto-permission-system/sandbox.claude.json .pi/sandbox.json
221
220
 
222
221
  # 2. Run Pi headlessly with complete automated protection:
223
222
  pi -p "Review pull request changes, verify dependencies, and execute test suite"
@@ -240,10 +239,10 @@ jobs:
240
239
  run: |
241
240
  npm install -g @earendil-works/pi-coding-agent
242
241
  pi install npm:auto-permission-system
243
- - name: Activate Cloud Auto Mode Preset
242
+ - name: Activate Claude Auto Mode Preset
244
243
  run: |
245
244
  mkdir -p .pi
246
- cp node_modules/auto-permission-system/sandbox.cloud.json .pi/sandbox.json
245
+ cp node_modules/auto-permission-system/sandbox.claude.json .pi/sandbox.json
247
246
  - name: Run Autonomous Review in Auto Mode
248
247
  run: |
249
248
  pi -p "Verify changes, run npm test, and flag security issues"
@@ -394,11 +393,11 @@ grep '"finding"' ~/.pi/agent/audit.log | tail -n 10
394
393
 
395
394
  A selection of common questions is below. For the complete, detailed guide, read **[docs/FAQ.md](docs/FAQ.md)**.
396
395
 
397
- ### How does Cloud Auto Mode secure unattended runs?
396
+ ### How does Claude Auto Mode secure unattended runs?
398
397
  In headless mode (`pi -p` or subagent executions), interactive ask prompts cannot be presented to a human. Auto Permission System operates in a fail-closed posture: any tool call or bash command targeting an unlisted domain, credential file, or outside-project path is automatically blocked, and the refusal reason is provided to the agent and recorded in the audit log.
399
398
 
400
- ### What is the relationship between Cloud Auto Mode and Claude's auto mode?
401
- Claude Code popularized autonomous "auto mode", allowing developers to let AI code without stopping for confirmation on harmless steps. Cloud Auto Mode brings this exact convenience to the Pi coding agent, but adds essential zero-trust boundaries: using Advanced Secure Mode to scan files, shell commands, and content to guarantee that autonomous execution never leads to data breaches or leaked API tokens.
399
+ ### What is the relationship between Claude Auto Mode and Claude's auto mode?
400
+ Claude Code popularized autonomous "auto mode", allowing developers to let AI code without stopping for confirmation on harmless steps. Claude Auto Mode brings this exact convenience to the Pi coding agent, but adds essential zero-trust boundaries: using Advanced Secure Mode to scan files, shell commands, and content to guarantee that autonomous execution never leads to data breaches or leaked API tokens.
402
401
 
403
402
  ### How do I allow the agent to reach a new domain?
404
403
  Add it to `network.allowedDomains` in `<cwd>/.pi/sandbox.json` or `~/.pi/agent/extensions/sandbox.json`, or run a curl command once and select **Yes, always… → All in group in this project**.
@@ -65,7 +65,7 @@ async function askDetection(
65
65
  if (choice === "session") {
66
66
  detectionGrants.add(key);
67
67
  audit({ layer: 2, tool: kind, subject, reason, decision: "session", note: "advanced-secure", cwd: ctx.cwd });
68
- ctx.ui.notify(`security-guard: allowed for this session (not saved) → ${subject}`, "info");
68
+ ctx.ui.notify(`security-guard: allowed for this conversation (not saved) → ${subject}`, "info");
69
69
  return null;
70
70
  }
71
71
  audit({ layer: 2, tool: kind, subject, reason, decision: "no", note: "advanced-secure", cwd: ctx.cwd });
@@ -91,7 +91,22 @@ async function detectMcpAsk(
91
91
  policy: mcpPolicy,
92
92
  });
93
93
  if (result.decision !== "ask") return null;
94
- return askDetection(ctx, "tool", toolName, result.summary);
94
+
95
+ // Include detailed call parameters or script preview if present
96
+ let detailedSummary = result.summary;
97
+ if (input && Object.keys(input).length > 0) {
98
+ const rawScript = input.code ?? input.script ?? input.command ?? input.query;
99
+ if (typeof rawScript === "string" && rawScript.includes("\n")) {
100
+ detailedSummary += `\n\n${rawScript}`;
101
+ } else {
102
+ const formattedArgs = JSON.stringify(input, null, 2);
103
+ if (formattedArgs.includes("\n")) {
104
+ detailedSummary += `\n\nArguments:\n${formattedArgs}`;
105
+ }
106
+ }
107
+ }
108
+
109
+ return askDetection(ctx, "tool", toolName, detailedSummary);
95
110
  }
96
111
 
97
112
  export default function (pi: ExtensionAPI) {
@@ -288,7 +303,7 @@ export default function (pi: ExtensionAPI) {
288
303
  if (typeof rawPath === "string" && rawPath) return rawPath;
289
304
  if (event.toolName === "bash") {
290
305
  const cmd = (event.input as { command?: string } | undefined)?.command;
291
- return cmd ? `bash: ${cmd}` : "bash";
306
+ return cmd ? `bash (${cmd})` : "bash";
292
307
  }
293
308
  return event.toolName;
294
309
  })();
@@ -301,15 +316,15 @@ export default function (pi: ExtensionAPI) {
301
316
  if (choice === "program-session" && programId) {
302
317
  grantProgramOutputSession(programId);
303
318
  audit({ layer: 2, tool: event.toolName, decision: "session", grant: programId, note: "advanced-secure-output", findings, cwd: ctx.cwd });
304
- ctx.ui.notify(`🛡 Advanced Secure: you allowed all ${programId} output for the rest of this session.`, "info");
319
+ ctx.ui.notify(`🧠 Advanced Secure: you allowed all ${programId} output for the rest of this conversation.`, "info");
305
320
  } else {
306
321
  audit({ layer: 2, tool: event.toolName, decision: "yes", note: "advanced-secure-output", findings, cwd: ctx.cwd });
307
- ctx.ui.notify(`🛡 Advanced Secure: you allowed ${event.toolName} output to be sent.`, "warning");
322
+ ctx.ui.notify(`🧠 Advanced Secure: you allowed ${event.toolName} output to be sent.`, "warning");
308
323
  }
309
324
  return;
310
325
  }
311
326
  audit({ layer: 2, tool: event.toolName, decision: "no", note: "advanced-secure-output", findings, cwd: ctx.cwd });
312
- ctx.ui.notify(`🛡 Advanced Secure: blocked ${event.toolName} output — it may contain sensitive information. Nothing was sent to the model.`, "warning");
327
+ ctx.ui.notify(`🧠 Advanced Secure: blocked ${event.toolName} output — it may contain sensitive information. Nothing was sent to the model.`, "warning");
313
328
  return { content: [{ type: "text", text: withheldNotice(subject) }] };
314
329
  });
315
330
 
@@ -349,7 +364,7 @@ export default function (pi: ExtensionAPI) {
349
364
  }
350
365
  recordProjectTrust(file, TRUST_STORE);
351
366
  audit({ layer: 2, event: "project-trusted", file, applied: changes, cwd: ctx.cwd });
352
- ctx.ui.notify("Done. The file applies now. Start a new session so the bash sandbox uses it too.", "info");
367
+ ctx.ui.notify("Done. The file applies now. Start a new conversation so the bash sandbox uses it too.", "info");
353
368
  return;
354
369
  }
355
370
  if (!active) {
@@ -367,7 +382,7 @@ export default function (pi: ExtensionAPI) {
367
382
  ` cwd: ${ctx.cwd}`,
368
383
  ` hasUI: ${(ctx as { hasUI?: boolean }).hasUI !== false}`,
369
384
  ` subagent.network: ${policy.subagent?.network ?? "allow"}`,
370
- ` session grants: ${sessionGrantSummary() || "(none)"}`,
385
+ ` conversation grants: ${sessionGrantSummary() || "(none)"}`,
371
386
  ` project file: ${!existsSync(projectPolicyPath(ctx.cwd)) ? "none" : projectTrusted(ctx.cwd) ? "trusted" : untrustedProjectChanges(ctx.cwd).length ? `not trusted, its weaker settings are ignored (/security trust)` : "only block rules, they apply"}`,
372
387
  ` outside project: read ${policy.filesystem.outsideProject?.read ?? "allow"}${(policy.filesystem.outsideProject?.allowRead ?? []).length ? `, allowRead ${policy.filesystem.outsideProject?.allowRead?.join(", ")}` : ""}`,
373
388
  "",
@@ -46,7 +46,7 @@ const EXPLANATION = [
46
46
  " No file contents are inspected.",
47
47
  "",
48
48
  "YOLO",
49
- " Turns every layer off. Saved like the other modes, so new sessions",
49
+ " Turns every layer off. Saved like the other modes, so new conversations",
50
50
  " start with it too until you pick Default or Advanced Secure again.",
51
51
  ].join("\n");
52
52
 
@@ -72,8 +72,8 @@ function renderSandboxChip(ctx: ExtensionContext): void {
72
72
  }
73
73
 
74
74
  function modeNotice(mode: PermissionMode): { text: string; level: "info" | "warning" } {
75
- if (mode === "yolo") return { text: "⚠️ YOLO — all security layers disabled. Saved; new sessions start this way too.", level: "warning" };
76
- if (mode === "advanced-secure") return { text: "🛡 Advanced Secure on. File reads, MCP calls, and tool output are checked for secrets.", level: "info" };
75
+ if (mode === "yolo") return { text: "⚠️ YOLO — all security layers disabled. Saved; new conversations start this way too.", level: "warning" };
76
+ if (mode === "advanced-secure") return { text: "🧠 Advanced Secure on. File reads, MCP calls, and tool output are checked for secrets.", level: "info" };
77
77
  return { text: "🛡 Default mode. Rule-based policy only.", level: "info" };
78
78
  }
79
79
 
@@ -92,7 +92,7 @@ async function pickMode(pi: ExtensionAPI, ctx: ExtensionContext, yolo: { active:
92
92
  emitYolo(pi.events, true);
93
93
  audit({ layer: 0, event: "permission-mode", mode: "yolo", cwd: ctx.cwd, note: "runtime-sync" });
94
94
  renderSandboxChip(ctx);
95
- ctx.ui.notify("⚠️ YOLO — all security layers disabled for this session.", "warning");
95
+ ctx.ui.notify("⚠️ YOLO — all security layers disabled for this conversation.", "warning");
96
96
  return;
97
97
  }
98
98
  ctx.ui.notify(`Permission mode is already ${modeLabel(next)}.`, "info");
@@ -218,7 +218,7 @@ export default function (pi: ExtensionAPI) {
218
218
  ` Allow Write: ${config.filesystem?.allowWrite?.join(", ") || "(none)"}`,
219
219
  ` Deny Write: ${config.filesystem?.denyWrite?.join(", ") || "(none)"}`,
220
220
  ` Outside project reads: ${config.filesystem?.outsideProject?.read ?? "allow"}`,
221
- ` Session grants: ${filesystemSessionGrantSummary() || "(none)"}`,
221
+ ` Conversation grants: ${filesystemSessionGrantSummary() || "(none)"}`,
222
222
  ];
223
223
  ctx.ui.notify(lines.join("\n"), "info");
224
224
  },
package/package.json CHANGED
@@ -1,36 +1,34 @@
1
1
  {
2
2
  "name": "auto-permission-system",
3
- "version": "3.9.0",
4
- "description": "Secure auto mode & cloud auto mode for Pi. A zero-trust permission system for the Pi coding agent powered by Advanced Secure mode to scan files, content, and tools to prevent data breaches.",
3
+ "version": "3.12.0",
4
+ "description": "Claude auto mode for Pi & security for Pi. A zero-trust permission system for Pi coding agent powered by Advanced Secure mode to scan files, content, and tools to prevent data breaches.",
5
5
  "type": "module",
6
6
  "author": "Navin Kumar",
7
7
  "keywords": [
8
8
  "pi-package",
9
9
  "pi",
10
10
  "pi-coding-agent",
11
+ "claude-auto-mode-for-pi",
11
12
  "pi-extension",
12
13
  "pi-permission",
13
14
  "pi-permissions",
14
15
  "pi-permission-system",
15
16
  "pi-security",
16
- "cloud-auto-mode-for-pi",
17
17
  "security-for-pi",
18
18
  "permission-system-for-pi",
19
19
  "secure-pi",
20
- "cloud-auto-mode",
21
- "pi-cloud-auto-mode",
20
+ "claude-auto-mode",
21
+ "pi-claude-auto-mode",
22
22
  "auto-permission-system",
23
23
  "auto-secure",
24
24
  "secure",
25
25
  "secure-it",
26
26
  "auto-mode",
27
27
  "safe-auto-mode",
28
- "claude-auto-mode",
29
28
  "data-breach-prevention",
30
29
  "content-scan",
31
- "cloud",
32
- "cloud-security",
33
- "auto-mode",
30
+ "claude",
31
+ "claude-security",
34
32
  "permission",
35
33
  "permissions",
36
34
  "permission-system",
@@ -91,7 +89,7 @@
91
89
  "assets/",
92
90
  "sandbox.example.json",
93
91
  "sandbox.default.json",
94
- "sandbox.cloud.json",
92
+ "sandbox.claude.json",
95
93
  "banner.png",
96
94
  "README.md",
97
95
  "CHANGELOG.md",
@@ -2,6 +2,7 @@
2
2
  "enabled": true,
3
3
  "mode": "advanced-secure",
4
4
  "network": {
5
+ "allowLocalhost": false,
5
6
  "strictAllowlist": true,
6
7
  "allowedDomains": [
7
8
  "registry.npmjs.org",
@@ -2,6 +2,7 @@
2
2
  "enabled": true,
3
3
  "mode": "advanced-secure",
4
4
  "network": {
5
+ "allowLocalhost": true,
5
6
  "allowedDomains": [
6
7
  "registry.npmjs.org",
7
8
  "registry.yarnpkg.com",
@@ -24,13 +25,22 @@
24
25
  "rubygems.org",
25
26
  "api.nuget.org",
26
27
  "deno.land",
27
- "jsr.io"
28
+ "jsr.io",
29
+ "api.openai.com",
30
+ "huggingface.co",
31
+ "*.huggingface.co",
32
+ "cdn-lfs.huggingface.co",
33
+ "*.diffusion.studio",
34
+ "diffusion.studio"
28
35
  ]
29
36
  },
30
37
  "filesystem": {
31
38
  "outsideProject": {
32
39
  "read": "allow",
33
- "allowRead": []
40
+ "allowRead": [
41
+ "~/Downloads",
42
+ "~/.cache"
43
+ ]
34
44
  },
35
45
  "denyRead": [],
36
46
  "askRead": [
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: auto-permission-system
3
- description: Use this skill when the user asks about auto-permission-system, security for Pi, permission system for Pi, how to secure Pi, Cloud auto mode for Pi, security policy configuration, why a tool call was blocked, how to add an allow-list entry, or how to configure sandbox behavior. Covers the two-layer sandbox (OS-level bash sandbox and in-process tool guard), policy file locations and fields, Cloud auto mode preset (sandbox.cloud.json), ask-tier prompt options, absolute-deny paths, escape hatches (--yolo, --no-sandbox, /permission-mode → YOLO), the audit log, and common tasks like allowing a new domain or write path.
3
+ description: Use this skill when the user asks about auto-permission-system, security for Pi, permission system for Pi, how to secure Pi, Claude auto mode for Pi, security policy configuration, why a tool call was blocked, how to add an allow-list entry, or how to configure sandbox behavior. Covers the two-layer sandbox (OS-level bash sandbox and in-process tool guard), policy file locations and fields, Claude auto mode preset (sandbox.claude.json), ask-tier prompt options, absolute-deny paths, escape hatches (--yolo, --no-sandbox, /permission-mode → YOLO), the audit log, and common tasks like allowing a new domain or write path.
4
4
  ---
5
5
 
6
6
  # Security for Pi (Pi Permission System) — Skill
7
7
 
8
- Use this skill when the user asks about the auto-permission-system extension, security for Pi, permission system for Pi, how to secure Pi, or configuring Cloud auto mode for Pi.
8
+ Use this skill when the user asks about the auto-permission-system extension, security for Pi, permission system for Pi, how to secure Pi, or configuring Claude auto mode for Pi.
9
9
 
10
10
  ## What this extension does
11
11
 
@@ -14,19 +14,19 @@ Wraps the `bash` tool with `sandbox-exec` (macOS) or `bubblewrap` (Linux). Block
14
14
  - Filesystem writes outside `allowWrite`
15
15
  - Filesystem reads of `denyRead` paths (defaults include `~/.ssh`, `~/.aws`, `~/.gnupg`, `~/.pi/agent`)
16
16
  - Filesystem reads outside the project when `outsideProject.read` is `"ask"` or `"deny"` — when set, the home dir (and the project's parent) is fenced with `denyRead` and the project plus `allowWrite`/`outsideProject.allowRead` are re-exposed (ADR-014). With `"ask"`, a bash command that *plainly* names an outside path is confirmed with you **before it runs** (ADR-015): allow (`yes — this once`, or a persistent `always` grant) → the real, complete output; deny → the command is not run at all (no silently trimmed output). `install`/`npm` under `~/.pi/agent` are pre-allowed via `outsideProject.allowRead`, so they never prompt. Obfuscated reads still hit the fence and stay masked.
17
- - Network egress to domains not in `allowedDomains`: a host in neither list asks before the proxy decides (ADR-023) — `yes — this once` covers that bash command's connections to the host, `yes — for this session` lasts until the session ends, and `always` writes `overrides.allowDomains` and applies live. A host in `deniedDomains`, or `network.strictAllowlist: true`, is a hard deny that is never ask-able; headless blocks.
17
+ - Network egress to domains not in `allowedDomains`: a host in neither list asks before the proxy decides (ADR-023) — `yes — this once` covers that bash command's connections to the host, `yes — for this conversation` lasts until the conversation ends, and `always` writes `overrides.allowDomains` and applies live. A host in `deniedDomains`, or `network.strictAllowlist: true`, is a hard deny that is never ask-able; headless blocks.
18
18
 
19
19
  **Layer 2 — In-process tool guard**
20
20
  Hooks `tool_call` for `read`, `grep`, `find`, `ls`, `write`, `edit`, `fetch_content`, `web_search`, `get_search_content`. Applies the same policy file as Layer 1. For `grep`, `find` and `ls` the search root is checked like a `read` path. A `tool_result` hook also removes `grep` output lines from denied files beneath an allowed root (for example a `.env` deep in the tree); approving the root does not approve those files. When a call is blocked, shows an interactive prompt with persistence options.
21
21
 
22
- **Layer 3 — Subagent posture & Cloud Auto Mode**
23
- When `ctx.hasUI === false` (subagents, `-p`, JSON mode, CI/CD runs), applies the `subagent.network` policy: `allow` (default) | `deny` | `research-only`. In Cloud Auto Mode, unapproved prompts fail closed.
22
+ **Layer 3 — Subagent posture & Claude Auto Mode**
23
+ When `ctx.hasUI === false` (subagents, `-p`, JSON mode, CI/CD runs), applies the `subagent.network` policy: `allow` (default) | `deny` | `research-only`. In Claude Auto Mode, unapproved prompts fail closed.
24
24
 
25
25
  ## Policy files
26
26
 
27
27
  Merged in order (later wins):
28
28
  1. `sandbox.default.json` — shipped baseline (`outsideProject.read: "ask"`, full `denyRead`/`denyWrite`, `modelDenyRead`)
29
- 2. `sandbox.cloud.json` — shipped Cloud Auto Mode preset for headless, Docker, and CI/CD runs (`strictAllowlist: true`, `outsideProject.read: "deny"`)
29
+ 2. `sandbox.claude.json` — shipped Claude Auto Mode preset for headless, Docker, and CI/CD runs (`strictAllowlist: true`, `outsideProject.read: "deny"`)
30
30
  3. `~/.pi/agent/extensions/sandbox.json` — global user policy
31
31
  4. `<cwd>/.pi/sandbox.json` — project-local overrides (also written by ask-tier prompts)
32
32
 
@@ -39,6 +39,7 @@ A higher layer only states what it changes; absent keys keep the layer below. A
39
39
  "enabled": true,
40
40
  "mode": "default", // "default" (rules) | "advanced-secure" (secret detection, ADR-018)
41
41
  "network": {
42
+ "allowLocalhost": true, // true (default): localhost on all ports allowed; false: strict network isolation
42
43
  "allowedDomains": ["github.com", "*.github.com"], // empty = allow all
43
44
  "deniedDomains": [], // hard deny, never ask-able
44
45
  "strictAllowlist": false // true = deny unknown hosts instead of asking
@@ -87,7 +88,7 @@ A project `.pi/sandbox.json` applies in full only after the user trusted its exa
87
88
  When a normal (non-absolute-deny) call is blocked:
88
89
  - **no — block** — hard deny (default, pre-selected)
89
90
  - **yes — this once** — allow just this call
90
- - **yes — for this session** — this file, its parent folder, or this domain until the session ends; not saved
91
+ - **yes — for this conversation** — this file, its parent folder, or this domain until the conversation ends; not saved
91
92
  - **always for CURRENT project** — whitelist this file in `<cwd>/.pi/sandbox.json`
92
93
  - **always for CURRENT project (folder)** — whitelist the parent directory
93
94
  - **always for ALL projects** — whitelist in `~/.pi/agent/extensions/sandbox.json`
@@ -99,7 +100,7 @@ For a Layer 1 bash **write** blocked after the command already ran, `yes — thi
99
100
 
100
101
  Every prompt waits **15 seconds** by default and shows it live as `Default: … (Ns)` under the body. For file reads/writes no answer means the safe default: **block / deny**, and Esc blocks. For a network ask, an unanswered countdown means **allow once** (Esc still denies) — ADR-024. In the TUI the `Default:` line disappears as soon as you press ↑/↓ and the timer restarts, so the hint never competes with an active choice (ADR-031). The prompt is keyboard-only (ADR-036): ↑/↓ move the highlight, Enter selects, Esc cancels — a mouse click or wheel over the prompt does nothing.
101
102
 
102
- The same options apply to a bash command whose network request reaches a host outside `allowedDomains` (ADR-023), with the countdown default and live footer from ADR-024/ADR-031: `No` / `Yes, just this once` / `Yes, all in group for this session` / `Yes, always…`, and an untouched countdown counts as allow-once. Because the sandbox proxy asks per connection, `Allow (default)` covers that bash command rather than one connection — redirects and parallel connections to the same host do not re-prompt. A host in `deniedDomains`, or `network.strictAllowlist: true`, is blocked outright: the prompt never appears. A remembered host is written to `overrides.allowDomains` and takes effect immediately, so the waiting request completes without a retry. Prompts from both layers are serialized through one queue (`src/ui/ask-flow.ts`), so two unknown hosts cannot replace each other's selector and hang the waiting connection.
103
+ The same options apply to a bash command whose network request reaches a host outside `allowedDomains` (ADR-023), with the countdown default and live footer from ADR-024/ADR-031: `No` / `Yes, just this once` / `Yes, all in group for this conversation` / `Yes, always…`, and an untouched countdown counts as allow-once. Because the sandbox proxy asks per connection, `Allow (default)` covers that bash command rather than one connection — redirects and parallel connections to the same host do not re-prompt. A host in `deniedDomains`, or `network.strictAllowlist: true`, is blocked outright: the prompt never appears. A remembered host is written to `overrides.allowDomains` and takes effect immediately, so the waiting request completes without a retry. Prompts from both layers are serialized through one queue (`src/ui/ask-flow.ts`), so two unknown hosts cannot replace each other's selector and hang the waiting connection.
103
104
 
104
105
  ## Sensitive reads ask
105
106
 
@@ -113,7 +114,7 @@ The same options apply to a bash command whose network request reaches a host ou
113
114
  - **`advanced-secure`** (recommended) — adds secret/credential detection, and **only adds asks or blocks** (never loosens):
114
115
  - file reads whose name looks like a credential store ask before the read;
115
116
  - risky `mcp__<server>__<tool>` calls ask before they run. The gate ignores the `mcp__<server>__` namespace, takes the operation from the head verb of the name (or the first verb of the description), and asks for destructive names, mutating names (`update_*`, `create_*`, `run_*`, `evaluate_*`), and destructive arguments (SQL `DELETE`/`UPDATE` without `WHERE`, `rm -rf`, `*` targets, HTTP `DELETE`, production targets). A documented tool with an unclassified name (`context`, `capture`, `logs`) does not ask; a tool with **no description** counts as opaque and does. Tune it under `mcp` in `sandbox.json` (ADR-032): `allowTools` / `allowPrefixes` skip asks, `askTools` forces them, `trustAnnotations` lets a server's `readOnlyHint` allow the call, `askThreshold` moves the score line (default 30). An untrusted project file may only tighten it (add `askTools`, lower `askThreshold`, turn `allowSimpleUpdates`/`trustAnnotations` off).
116
- - before any tool/command/file output reaches the model, a secret-like hit shows the file and each detected line as `lineNo: text`, with two choices — **No, keep private** or **Yes, allow**. No, keep private withholds the output and tells the model it was withheld because it may contain sensitive information (not a failure, not an empty result).
117
+ - before any tool/command/file output reaches the model, a secret-like hit shows the tool and each detected line as `Finding <lineNo>: <complete line>` — one `Finding` row per match, no truncation — plus `Question Should the AI be allowed to see it?`, with two choices — **No, keep private** or **Yes, allow**. No, keep private withholds the output and tells the model it was withheld because it may contain sensitive information (not a failure, not an empty result).
117
118
 
118
119
  Switch with `/permission-mode`; all three modes are saved to the global `sandbox.json` and reflected in the footer's sandbox chip (`Sandbox: 🧠 N domains, M paths` for advanced-secure, `Sandbox: 🔒 …` for default). An untrusted project may move the mode **up** (toward stricter), never down: it cannot turn on YOLO or turn off Advanced Secure. The third choice, **YOLO**, disables every layer; the chip reads `⚠️ YOLO — all security layers disabled` until you pick another mode (ADR-020).
119
120
 
@@ -31,6 +31,63 @@ export function isWriteBlockError(output: string): boolean {
31
31
  return WRITE_BLOCK_RE.test(output);
32
32
  }
33
33
 
34
+ /**
35
+ * Loopback / localhost connection refusal evidence in a sandboxed command's output.
36
+ * On Linux, bubblewrap runs with --unshare-net which isolates network namespaces.
37
+ * Host services listening on 127.0.0.1/localhost (like Diffusion Studio on port 3274,
38
+ * or local dev servers) are not reachable from inside the container namespace.
39
+ */
40
+ const LOOPBACK_REFUSAL_RE =
41
+ /(?:Failed to connect to (?:127\.0\.0\.1|localhost|::1)|(?:connect|connection refused).*?(?:127\.0\.0\.1|localhost|::1)|(?:127\.0\.0\.1|localhost|::1).*?(?:connection refused|ECONNREFUSED)|ECONNREFUSED.*?(?:127\.0\.0\.1|localhost|::1)|Diffusion Studio is not running)/i;
42
+
43
+ /** True when the command failed due to connecting to a loopback/localhost address in the sandbox. */
44
+ export function isLoopbackConnectionRefused(output: string): boolean {
45
+ return LOOPBACK_REFUSAL_RE.test(output);
46
+ }
47
+
48
+ export interface LoopbackEndpointInfo {
49
+ host: string;
50
+ port?: string;
51
+ service?: string;
52
+ }
53
+
54
+ /**
55
+ * Extract endpoint metadata (host, port, service name) from a loopback connection failure.
56
+ */
57
+ export function extractLoopbackEndpoint(output: string, command?: string): LoopbackEndpointInfo | undefined {
58
+ if (!isLoopbackConnectionRefused(output)) return undefined;
59
+
60
+ // Check for Diffusion Studio specific error or command
61
+ const isDiffusion = /diffusion studio is not running/i.test(output) || (command && /\b(?:dapi|diffusion)\b/i.test(command));
62
+ if (isDiffusion) {
63
+ return { host: "127.0.0.1", port: "3274", service: "Diffusion Studio" };
64
+ }
65
+
66
+ // Match "Failed to connect to 127.0.0.1 port 3274" or "localhost port 8080"
67
+ const curlMatch = output.match(/Failed to connect to (127\.0\.0\.1|localhost|::1) port (\d+)/i);
68
+ if (curlMatch) {
69
+ return {
70
+ host: curlMatch[1],
71
+ port: curlMatch[2],
72
+ service: curlMatch[2] === "3274" ? "Diffusion Studio" : undefined,
73
+ };
74
+ }
75
+
76
+ // Match "connect ECONNREFUSED 127.0.0.1:3274"
77
+ const nodeMatch = output.match(/(?:ECONNREFUSED|connect to)\s+(127\.0\.0\.1|localhost|::1)(?::| port )(\d+)/i);
78
+ if (nodeMatch) {
79
+ return {
80
+ host: nodeMatch[1],
81
+ port: nodeMatch[2],
82
+ service: nodeMatch[2] === "3274" ? "Diffusion Studio" : undefined,
83
+ };
84
+ }
85
+
86
+ // Generic host match
87
+ const hostMatch = output.match(/(127\.0\.0\.1|localhost|::1)/i);
88
+ return { host: hostMatch ? hostMatch[1] : "127.0.0.1" };
89
+ }
90
+
34
91
  /** A shell-name/empty token is not a path; everything else resolves against cwd unless absolute. */
35
92
  function toAbsolutePath(rawToken: string, cwd: string, home: string): string | undefined {
36
93
  let token = rawToken.trim().replace(/[.,;]+$/, "");
package/src/core/trust.ts CHANGED
@@ -67,7 +67,7 @@ export function isProjectFileDeclined(projectPath: string, storePath: string): b
67
67
  * and share only the store file (ADR-018/ADR-020).
68
68
  */
69
69
  export interface ProjectTrust {
70
- /** Record pi's own project-trust answer for this session. Declined is never trusted. */
70
+ /** Record pi's own project-trust answer for this conversation. Declined is never trusted. */
71
71
  setDeclined(value: boolean): void;
72
72
  /** The project file applies in full only when its current hash is recorded and pi did not decline. */
73
73
  isTrusted(cwd: string): boolean;
@@ -181,6 +181,33 @@ export const COMMON_NON_SECRET_VALUES = new Set([
181
181
  "secretvalue",
182
182
  "somevalue",
183
183
  "myvalue",
184
+ // Common keyboard keys / UI events (e.g. `Successfully pressed key: Backspace`)
185
+ "backspace",
186
+ "tab",
187
+ "enter",
188
+ "return",
189
+ "escape",
190
+ "esc",
191
+ "space",
192
+ "pageup",
193
+ "pagedown",
194
+ "end",
195
+ "home",
196
+ "arrowleft",
197
+ "arrowup",
198
+ "arrowright",
199
+ "arrowdown",
200
+ "left",
201
+ "up",
202
+ "right",
203
+ "down",
204
+ "delete",
205
+ "insert",
206
+ "capslock",
207
+ "numlock",
208
+ "scrolllock",
209
+ "printscreen",
210
+ "pause",
184
211
  ]);
185
212
 
186
213
  /**