@zhongqian97-code/ecode 0.3.5 → 0.3.7

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.
Files changed (2) hide show
  1. package/dist/index.js +64 -12
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -258,8 +258,9 @@ var ALLOWLIST = [
258
258
  "date",
259
259
  "whoami",
260
260
  "which",
261
- "env",
262
261
  "printenv"
262
+ // "env" 故意排除:env <CMD> 会执行任意子命令,不适合 allow 级别。
263
+ // 仅显示环境变量的 printenv 保留在白名单。
263
264
  ];
264
265
  var DEFAULT_DANGER_LIST = [
265
266
  "rm -rf",
@@ -284,7 +285,10 @@ var INDIRECT_EXEC_LIST = [
284
285
  "python3 -c",
285
286
  "node -e",
286
287
  "perl -e",
287
- "ruby -e"
288
+ "ruby -e",
289
+ // eval 执行任意字符串作为 shell 命令,无论 payload 内容如何都视为危险。
290
+ // 与 xargs/python -c 的性质相同:执行能力本身就是风险。
291
+ "eval"
288
292
  ];
289
293
  function hasFindExec(cmd) {
290
294
  return /^find(\s|$)/.test(cmd) && /\s-exec(\s|$)/.test(cmd);
@@ -348,22 +352,70 @@ function splitByControlOps(cmd) {
348
352
  }
349
353
  function stripShellWrapper(cmd) {
350
354
  const trimmed = cmd.trim();
351
- const dq = trimmed.match(/^(?:bash|sh)\s+-c\s+"((?:[^"\\]|\\.)*)"/);
352
- if (dq) return dq[1];
353
- const sq = trimmed.match(/^(?:bash|sh)\s+-c\s+'([^']*)'/);
354
- if (sq) return sq[1];
355
- const nq = trimmed.match(/^(?:bash|sh)\s+-c\s+(\S+)/);
356
- if (nq) return nq[1];
355
+ const shellMatch = trimmed.match(/^(bash|sh)\s+([\s\S]*)/);
356
+ if (!shellMatch) return trimmed;
357
+ let rest = shellMatch[2];
358
+ let hasCFlag = false;
359
+ while (rest.length > 0) {
360
+ const flagMatch = rest.match(/^(-\w+)\s*([\s\S]*)/);
361
+ if (!flagMatch) break;
362
+ const flag = flagMatch[1];
363
+ rest = flagMatch[2];
364
+ if (flag.includes("c")) {
365
+ hasCFlag = true;
366
+ rest = rest.trimStart();
367
+ break;
368
+ }
369
+ rest = rest.trimStart();
370
+ }
371
+ if (!hasCFlag || !rest.length) return trimmed;
372
+ if (rest.startsWith('"')) {
373
+ const m = rest.match(/^"((?:[^"\\]|\\.)*)"/);
374
+ if (m) return m[1];
375
+ } else if (rest.startsWith("'")) {
376
+ const m = rest.match(/^'([^']*)'/);
377
+ if (m) return m[1];
378
+ } else {
379
+ const m = rest.match(/^(\S+)/);
380
+ if (m) return m[1];
381
+ }
382
+ return trimmed;
383
+ }
384
+ function stripEnvWrapper(cmd) {
385
+ const trimmed = cmd.trim();
386
+ if (!trimmed.startsWith("env ") && trimmed !== "env") return trimmed;
387
+ let rest = trimmed.slice(3).trimStart();
388
+ while (rest.length > 0) {
389
+ const assignMatch = rest.match(/^(\w+=\S*)\s*([\s\S]*)/);
390
+ if (!assignMatch) break;
391
+ rest = assignMatch[2].trimStart();
392
+ }
393
+ if (!rest.length) return trimmed;
394
+ return rest;
395
+ }
396
+ function stripCommandWrapper(cmd) {
397
+ const trimmed = cmd.trim();
398
+ const m = trimmed.match(/^command\s+([\s\S]+)/);
399
+ if (m) return m[1].trimStart();
357
400
  return trimmed;
358
401
  }
359
402
  function classifyCommand(cmd, dangerPatterns, _depth = 0) {
360
403
  const trimmed = cmd.trim();
361
404
  const patterns = dangerPatterns ?? DEFAULT_DANGER_LIST;
362
405
  if (_depth < 5) {
363
- const inner = stripShellWrapper(trimmed);
364
- if (inner !== trimmed) {
365
- const innerClass = classifyCommand(inner.trim(), dangerPatterns, _depth + 1);
366
- if (innerClass === "danger") return "danger";
406
+ const shellInner = stripShellWrapper(trimmed);
407
+ if (shellInner !== trimmed) {
408
+ if (classifyCommand(shellInner.trim(), dangerPatterns, _depth + 1) === "danger") return "danger";
409
+ return "normal";
410
+ }
411
+ const envInner = stripEnvWrapper(trimmed);
412
+ if (envInner !== trimmed) {
413
+ if (classifyCommand(envInner.trim(), dangerPatterns, _depth + 1) === "danger") return "danger";
414
+ return "normal";
415
+ }
416
+ const commandInner = stripCommandWrapper(trimmed);
417
+ if (commandInner !== trimmed) {
418
+ if (classifyCommand(commandInner.trim(), dangerPatterns, _depth + 1) === "danger") return "danger";
367
419
  return "normal";
368
420
  }
369
421
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhongqian97-code/ecode",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "description": "A minimal Claude Code clone with REPL interface and bash tool calling",
5
5
  "type": "module",
6
6
  "author": "zhongqian97-code",