zames_pro 2.90.12 → 2.90.14

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
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.90.14] - 2026-10-29
11
+
12
+ ### Fixed
13
+
14
+ - **sandbox:** block a recursive rm of an absolute or outside path
15
+
16
+ ## [2.90.13] - 2026-10-29
17
+
18
+ ### Fixed
19
+
20
+ - **apply-patch:** start each hunk search where the previous one ended
21
+
10
22
  ## [2.90.12] - 2026-10-29
11
23
 
12
24
  ### Fixed
@@ -168,6 +168,11 @@ export function applyUpdateHunk(content, hunkLines) {
168
168
  const hasCRLF = content.includes('\r\n');
169
169
  const eol = hasCRLF ? '\r\n' : NL;
170
170
  let fileLines = content.split(/\r?\n/);
171
+ // Hunks are ordered top-to-bottom, so the search for the next block must not
172
+ // go back before the end of the previous one. Searching from 0 made a hunk
173
+ // re-find a region an EARLIER hunk had already consumed (or, once that text
174
+ // was gone, fail with "Context not found").
175
+ let searchFrom = 0;
171
176
  for (const block of splitHunks(hunkLines)) {
172
177
  const oldLines = [];
173
178
  const newLines = [];
@@ -198,7 +203,7 @@ export function applyUpdateHunk(content, hunkLines) {
198
203
  error: 'Hunk has no context or removed lines to anchor on.',
199
204
  };
200
205
  }
201
- const at = locateBlock(fileLines, oldLines, block.oldStart, 0);
206
+ const at = locateBlock(fileLines, oldLines, block.oldStart, searchFrom);
202
207
  if (at === -1) {
203
208
  return {
204
209
  ok: false,
@@ -211,6 +216,9 @@ export function applyUpdateHunk(content, hunkLines) {
211
216
  ...newLines,
212
217
  ...fileLines.slice(at + oldLines.length),
213
218
  ];
219
+ // The next block starts after the lines this hunk just produced: its text
220
+ // cannot legitimately live BEFORE them.
221
+ searchFrom = at + newLines.length;
214
222
  }
215
223
  return { ok: true, content: fileLines.join(eol) };
216
224
  }
package/dist/shell.js CHANGED
@@ -70,6 +70,48 @@ export function assertCommandInsideRoot(root, command) {
70
70
  if (/\bchroot\b/i.test(cmd)) {
71
71
  throw new Error('Sandbox: chroot is forbidden');
72
72
  }
73
+ // A RECURSIVE deletion aimed at an absolute path, `~`, `.`, `..` or the root
74
+ // itself can never be "inside the root": a single typo in a cleanup command
75
+ // destroyed the whole project tree (and the directories above it) in a real
76
+ // session. Only the destructive form is refused — `rm file.txt` and
77
+ // `rm -rf tmp/build` stay allowed.
78
+ const rmRe = /(?:^|[;&|(]\s*)\s*(?:sudo\s+)?rm\s+([^;&|)]+)/gi;
79
+ let rm;
80
+ while ((rm = rmRe.exec(cmd))) {
81
+ const words = rm[1].trim().split(/\s+/);
82
+ const recursive = words.some((w) => w === '--recursive' || /^-[a-zA-Z]*[rR][a-zA-Z]*$/.test(w));
83
+ if (!recursive)
84
+ continue;
85
+ for (const w of words) {
86
+ if (w.startsWith('-'))
87
+ continue;
88
+ const raw = w.replace(/["']/g, '');
89
+ if (!raw)
90
+ continue;
91
+ if (raw === '/' ||
92
+ raw === '.' ||
93
+ raw === '..' ||
94
+ raw === '~' ||
95
+ raw.startsWith('~/') ||
96
+ raw.startsWith('/') ||
97
+ raw.startsWith('..')) {
98
+ throw new Error('Sandbox: recursive rm of an absolute/outside path is forbidden (' +
99
+ raw +
100
+ ')');
101
+ }
102
+ // A RELATIVE target is resolved against the root; anything that escapes
103
+ // (a `../` inside a longer path) is refused too.
104
+ const target = path.resolve(root, raw);
105
+ const rel = path.relative(root, target);
106
+ if (rel.startsWith('..') || path.isAbsolute(rel)) {
107
+ throw new Error('Sandbox: recursive rm outside ' +
108
+ root +
109
+ ' is forbidden (' +
110
+ raw +
111
+ ')');
112
+ }
113
+ }
114
+ }
73
115
  }
74
116
  function formatExecResult(err, stdout, stderr, timeout, okExitCodes) {
75
117
  const out = (stdout || '').toString();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zames_pro",
3
- "version": "2.90.12",
3
+ "version": "2.90.14",
4
4
  "description": "Terminal coding agent that drives a real browser to chat with DeepSeek, Qwen or GLM — no API key required. Reads and edits files, runs commands, commits to git, like Claude Code / Codex CLI.",
5
5
  "type": "module",
6
6
  "bin": {