@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
|
-
|
|
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
|
@@ -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
|
-
//
|
|
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}
|