@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.
- package/dist/index.js +64 -12
- 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
|
|
352
|
-
if (
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
|
364
|
-
if (
|
|
365
|
-
|
|
366
|
-
|
|
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
|
}
|