@iinm/plain-agent 1.8.9 → 1.8.11

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 CHANGED
@@ -322,9 +322,13 @@ Files are loaded in the following order. Settings in later files override earlie
322
322
  └── .plain-agent/
323
323
  ├── (3) config.json # Project-specific configuration
324
324
  ├── (4) config.local.json # Project-specific local configuration (including secrets)
325
- ├── memory/ # Task-specific memory files
325
+ ├── memory/ # Task-specific memory files (auto-approvable)
326
+ ├── tmp/ # Agent scratch space (auto-approvable)
327
+ ├── claude-code-plugins/ # Cached Claude Code plugins
326
328
  ├── prompts/ # Project-specific prompts
327
- └── agents/ # Project-specific agent roles
329
+ ├── agents/ # Project-specific agent roles
330
+ ├── sandbox/ # Sandbox runner scripts (run.sh, Dockerfile); always require approval
331
+ └── setup.sh # Initial setup script
328
332
  ```
329
333
 
330
334
  ### Example
@@ -75,8 +75,19 @@ Generate `.plain-agent/sandbox/run.sh`. Use the following Node.js example as the
75
75
 
76
76
  set -eu -o pipefail
77
77
 
78
+ # Mount .plain-agent/ as read-only over the writable project root, then
79
+ # re-overlay only memory/ and tmp/ as writable scratch space.
80
+ working_dir=$(pwd)
81
+ metadata_dir="$working_dir/.plain-agent"
82
+ mkdir -p \
83
+ "$metadata_dir/memory" \
84
+ "$metadata_dir/tmp"
85
+
78
86
  options=(
79
87
  --allow-write
88
+ --mount-readonly "$metadata_dir:$metadata_dir"
89
+ --mount-writable "$metadata_dir/memory:$metadata_dir/memory"
90
+ --mount-writable "$metadata_dir/tmp:$metadata_dir/tmp"
80
91
  --volume plain-sandbox--global--home-npm:/home/sandbox/.npm
81
92
  --volume node_modules
82
93
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iinm/plain-agent",
3
- "version": "1.8.9",
3
+ "version": "1.8.11",
4
4
  "description": "A lightweight CLI-based coding agent",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -3,11 +3,23 @@ import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import {
5
5
  AGENT_MEMORY_DIR,
6
+ AGENT_PROJECT_METADATA_DIR,
6
7
  AGENT_TMP_DIR,
7
8
  CLAUDE_CODE_PLUGIN_DIR,
8
9
  } from "./env.mjs";
9
10
  import { noThrowSync } from "./utils/noThrow.mjs";
10
11
 
12
+ // Paths that must never be auto-approvable as tool input, even when
13
+ // git-managed. Sandbox scripts run on the host and the project config files
14
+ // drive auto-approval policy itself, so silent in-sandbox modification of
15
+ // either could lead to host code execution or self-granted privilege
16
+ // escalation.
17
+ const UNSAFE_PROJECT_PATHS = [
18
+ path.join(AGENT_PROJECT_METADATA_DIR, "sandbox"),
19
+ path.join(AGENT_PROJECT_METADATA_DIR, "config.json"),
20
+ path.join(AGENT_PROJECT_METADATA_DIR, "config.local.json"),
21
+ ];
22
+
11
23
  /**
12
24
  * @param {unknown} input
13
25
  * @returns {boolean}
@@ -64,7 +76,12 @@ export function isSafeToolInputItem(arg) {
64
76
  return false;
65
77
  }
66
78
 
67
- // Allow safe path even if git-ignored.
79
+ // Always require approval for these, even if git-managed.
80
+ if (isUnsafeProjectPath(realPath)) {
81
+ return false;
82
+ }
83
+
84
+ // Always allow these even if git-ignored.
68
85
  if (isSafePath(realPath)) {
69
86
  return true;
70
87
  }
@@ -155,6 +172,24 @@ function isSafePath(targetPath) {
155
172
  return false;
156
173
  }
157
174
 
175
+ /**
176
+ * @param {string} targetPath
177
+ * @returns {boolean}
178
+ */
179
+ function isUnsafeProjectPath(targetPath) {
180
+ for (const unsafePath of UNSAFE_PROJECT_PATHS) {
181
+ const unsafeAbsPath = path.resolve(unsafePath);
182
+ if (
183
+ targetPath === unsafeAbsPath ||
184
+ targetPath.startsWith(`${unsafeAbsPath}${path.sep}`)
185
+ ) {
186
+ return true;
187
+ }
188
+ }
189
+
190
+ return false;
191
+ }
192
+
158
193
  /**
159
194
  * @param {string} absPath
160
195
  * @returns {boolean}