@kenhkl/agent-moss 0.16.5 → 0.16.6
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/cli.js +386 -53
- package/dist/engine/coordinator.d.ts +7 -1
- package/dist/engine/coordinator.js +361 -49
- package/dist/rules/l1_rules.json +63 -3
- package/dist/server.js +386 -53
- package/dist/skills/file_access_guard.md +3 -2
- package/dist/skills/lateral_movement_guard.md +3 -2
- package/dist/skills/persistence_backdoor_guard.md +4 -2
- package/dist/skills/supply_chain_guard.md +1 -0
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -110,18 +110,41 @@ function hasCommandSubstitution(text) {
|
|
|
110
110
|
return CMD_SUBSTITUTION.test(text);
|
|
111
111
|
}
|
|
112
112
|
function splitCommandSegments(command) {
|
|
113
|
-
|
|
113
|
+
const segments = [];
|
|
114
|
+
let cur = "";
|
|
115
|
+
let quote = null;
|
|
116
|
+
for (let i = 0; i < command.length; i++) {
|
|
117
|
+
const ch = command[i];
|
|
118
|
+
if (quote) {
|
|
119
|
+
cur += ch;
|
|
120
|
+
if (ch === quote) quote = null;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (ch === "'" || ch === '"') {
|
|
124
|
+
quote = ch;
|
|
125
|
+
cur += ch;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (/\||&|;|\n/.test(ch)) {
|
|
129
|
+
segments.push(cur);
|
|
130
|
+
segments.push(ch);
|
|
131
|
+
cur = "";
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
cur += ch;
|
|
135
|
+
}
|
|
136
|
+
if (cur) segments.push(cur);
|
|
137
|
+
return segments.map((s) => s.trim()).filter((s) => s.length > 0 && !/^(?:\|\||&&|[|;&])$/.test(s));
|
|
114
138
|
}
|
|
115
139
|
function extractFirstCommand(command) {
|
|
116
140
|
const segments = splitCommandSegments(command);
|
|
117
141
|
return segments.length ? segments[0] : "";
|
|
118
142
|
}
|
|
119
|
-
var
|
|
143
|
+
var CMD_SUBSTITUTION;
|
|
120
144
|
var init_shell_split = __esm({
|
|
121
145
|
"src/engine/shell-split.ts"() {
|
|
122
146
|
"use strict";
|
|
123
147
|
init_esm_shims();
|
|
124
|
-
CMD_SEPARATORS = /\|\||&&|[|;&\n]/;
|
|
125
148
|
CMD_SUBSTITUTION = /\$\(|`|</;
|
|
126
149
|
}
|
|
127
150
|
});
|
|
@@ -372,20 +395,25 @@ var init_runtime_config_bridge = __esm({
|
|
|
372
395
|
});
|
|
373
396
|
|
|
374
397
|
// src/infra/rules-loader.ts
|
|
375
|
-
import { readFileSync as readFileSync2, existsSync } from "fs";
|
|
376
|
-
import { resolve, dirname } from "path";
|
|
398
|
+
import { readFileSync as readFileSync2, existsSync, readdirSync, statSync } from "fs";
|
|
399
|
+
import { resolve, dirname, join as join2 } from "path";
|
|
377
400
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
378
401
|
function dataRootDir() {
|
|
379
402
|
return getDataRoot();
|
|
380
403
|
}
|
|
381
|
-
function
|
|
382
|
-
|
|
404
|
+
function parseDirEnv(envName) {
|
|
405
|
+
const raw = (process.env[envName] || "").trim();
|
|
406
|
+
if (!raw) return [];
|
|
407
|
+
return raw.split(/[,;]/).map((s) => s.trim()).filter(Boolean).map((s) => resolve(s));
|
|
408
|
+
}
|
|
409
|
+
function overrideRulesDirs() {
|
|
410
|
+
return [...parseDirEnv("AGENT_MOSS_OVERRIDE_RULES_DIR"), resolve(dataRootDir(), "rules")];
|
|
383
411
|
}
|
|
384
412
|
function warnOverride(name, path6, data) {
|
|
385
413
|
if (warnedOverride.has(path6)) return;
|
|
386
414
|
warnedOverride.add(path6);
|
|
387
415
|
console.warn(
|
|
388
|
-
`[AgentMoss] \u89C4\u5219\u6587\u4EF6\u88AB\
|
|
416
|
+
`[AgentMoss] \u89C4\u5219\u6587\u4EF6\u88AB\u8986\u76D6\u76EE\u5F55\u8986\u76D6: ${path6}\uFF08\u5185\u7F6E\u9ED8\u8BA4\u88AB\u5FFD\u7565\uFF09\u3002\u8BE5\u6587\u4EF6\u7531\u90E8\u7F72\u4FA7\u81EA\u884C\u7EF4\u62A4 \u2014\u2014 **\u5185\u7F6E\u89C4\u5219\u66F4\u65B0\u4E0D\u4F1A\u53CD\u6620\u5230\u5B83\u4E0A\u9762**\uFF1B\u5220\u9664\u8BE5\u6587\u4EF6\uFF08\u6216\u6E05\u7A7A AGENT_MOSS_OVERRIDE_RULES_DIR\uFF09\u5373\u6062\u590D\u5185\u7F6E\u89C4\u5219\u3002\u82E5\u53EA\u60F3\u9010\u6761\u542F\u505C/\u589E\u5220\uFF0C\u8BF7\u6539\u7528 runtime config\uFF08Console\uFF09\u3002`
|
|
389
417
|
);
|
|
390
418
|
const missing = (EXPECTED_KEYS[name] || []).filter((k) => !(k in data));
|
|
391
419
|
if (missing.length > 0) {
|
|
@@ -397,24 +425,34 @@ function warnOverride(name, path6, data) {
|
|
|
397
425
|
function overriddenRuleFiles() {
|
|
398
426
|
const out = [];
|
|
399
427
|
for (const name of Object.keys(EXPECTED_KEYS)) {
|
|
400
|
-
const
|
|
401
|
-
|
|
428
|
+
for (const dir of overrideRulesDirs()) {
|
|
429
|
+
const p = resolve(dir, name);
|
|
430
|
+
if (existsSync(p)) {
|
|
431
|
+
out.push(p);
|
|
432
|
+
break;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
402
435
|
}
|
|
403
436
|
return out;
|
|
404
437
|
}
|
|
405
438
|
function findRulesFile(name) {
|
|
406
|
-
const
|
|
407
|
-
|
|
439
|
+
for (const dir of overrideRulesDirs()) {
|
|
440
|
+
const p = resolve(dir, name);
|
|
441
|
+
if (existsSync(p)) return p;
|
|
442
|
+
}
|
|
408
443
|
for (const dir of RULES_DIR_CANDS) {
|
|
409
444
|
const p = resolve(dir, name);
|
|
410
445
|
if (existsSync(p)) return p;
|
|
411
446
|
}
|
|
412
447
|
return null;
|
|
413
448
|
}
|
|
449
|
+
function isOverridePath(name, p) {
|
|
450
|
+
return overrideRulesDirs().some((dir) => resolve(dir, name) === p);
|
|
451
|
+
}
|
|
414
452
|
function loadJson(name) {
|
|
415
453
|
const p = findRulesFile(name);
|
|
416
454
|
if (!p) return {};
|
|
417
|
-
const isOverride =
|
|
455
|
+
const isOverride = isOverridePath(name, p);
|
|
418
456
|
let data;
|
|
419
457
|
try {
|
|
420
458
|
data = JSON.parse(readFileSync2(p, "utf-8")) || {};
|
|
@@ -425,6 +463,14 @@ function loadJson(name) {
|
|
|
425
463
|
if (isOverride) warnOverride(name, p, data);
|
|
426
464
|
return data;
|
|
427
465
|
}
|
|
466
|
+
function resolveScenarioRulesFile(scenarioId) {
|
|
467
|
+
const rel = join2("scenarios", scenarioId, "rules.json");
|
|
468
|
+
for (const dir of overrideRulesDirs()) {
|
|
469
|
+
const p = resolve(dir, rel);
|
|
470
|
+
if (existsSync(p)) return p;
|
|
471
|
+
}
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
428
474
|
function loadL1() {
|
|
429
475
|
return loadJson("l1_rules.json");
|
|
430
476
|
}
|
|
@@ -457,7 +503,142 @@ function loadMC() {
|
|
|
457
503
|
function loadToolRegistry() {
|
|
458
504
|
return loadJson("tool_registry.json");
|
|
459
505
|
}
|
|
460
|
-
|
|
506
|
+
function extraRulesDirs() {
|
|
507
|
+
return parseDirEnv("AGENT_MOSS_EXTRA_RULES_DIR");
|
|
508
|
+
}
|
|
509
|
+
function listJsonFilesDeep(dir) {
|
|
510
|
+
const out = [];
|
|
511
|
+
let entries;
|
|
512
|
+
try {
|
|
513
|
+
entries = readdirSync(dir, { withFileTypes: true }).map((d) => d.name);
|
|
514
|
+
} catch {
|
|
515
|
+
return out;
|
|
516
|
+
}
|
|
517
|
+
for (const name of entries.sort()) {
|
|
518
|
+
const p = resolve(dir, name);
|
|
519
|
+
let isDir = false;
|
|
520
|
+
try {
|
|
521
|
+
isDir = statSync(p).isDirectory();
|
|
522
|
+
} catch {
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
if (isDir) out.push(...listJsonFilesDeep(p));
|
|
526
|
+
else if (name.toLowerCase().endsWith(".json")) out.push(p);
|
|
527
|
+
}
|
|
528
|
+
return out;
|
|
529
|
+
}
|
|
530
|
+
function scanExtraFiles(files) {
|
|
531
|
+
const data = {};
|
|
532
|
+
const loaded = [];
|
|
533
|
+
const seenIds = /* @__PURE__ */ new Map();
|
|
534
|
+
const classifyFlat = (r) => {
|
|
535
|
+
for (const [keys, cat] of EXTRA_FLAT_FIELD) {
|
|
536
|
+
if (r && typeof r === "object" && keys.every((k) => r[k])) return cat;
|
|
537
|
+
}
|
|
538
|
+
return null;
|
|
539
|
+
};
|
|
540
|
+
for (const p of files) {
|
|
541
|
+
let parsed;
|
|
542
|
+
try {
|
|
543
|
+
parsed = JSON.parse(readFileSync2(p, "utf-8"));
|
|
544
|
+
} catch (e) {
|
|
545
|
+
console.warn(`[AgentMoss] \u8FFD\u52A0\u89C4\u5219\u6587\u4EF6\u635F\u574F/\u4E0D\u53EF\u8BFB\uFF0C\u5DF2\u8DF3\u8FC7: ${p}\uFF08${e.message}\uFF09`);
|
|
546
|
+
continue;
|
|
547
|
+
}
|
|
548
|
+
let pairs = [];
|
|
549
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
550
|
+
pairs = Object.entries(parsed);
|
|
551
|
+
} else if (Array.isArray(parsed)) {
|
|
552
|
+
let flatBad = 0;
|
|
553
|
+
for (const r of parsed) {
|
|
554
|
+
const cat = classifyFlat(r);
|
|
555
|
+
if (cat === null) {
|
|
556
|
+
flatBad += 1;
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
pairs.push([cat, [r]]);
|
|
560
|
+
}
|
|
561
|
+
if (flatBad > 0) {
|
|
562
|
+
console.warn(
|
|
563
|
+
`[AgentMoss] \u8FFD\u52A0\u89C4\u5219\u6587\u4EF6 ${p} \u6709 ${flatBad} \u6761\u5E73\u94FA\u6761\u76EE\u65E0\u6CD5\u5F52\u7C7B\uFF08\u88F8 pattern \u4E0D\u5E26 risk_level/risk_type \u65F6\u533A\u5206\u4E0D\u4E86 L1 \u5371\u9669\u547D\u4EE4\u4E0E L2 \u6388\u6743\u7C7B\uFF09\u2014\u2014 \u8BF7\u6539\u7528\u5BF9\u8C61\u5F62\u5F0F\uFF1A{"\u7C7B\u522B\u952E": [\u6761\u76EE...]}\u3002`
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
} else {
|
|
567
|
+
console.warn(
|
|
568
|
+
`[AgentMoss] \u8FFD\u52A0\u89C4\u5219\u6587\u4EF6 ${p} \u9876\u5C42\u5E94\u662F\u5BF9\u8C61\uFF08\u7C7B\u522B\u952E\u2192\u6761\u76EE\u6570\u7EC4\uFF09\u6216\u6570\u7EC4\uFF0C\u5DF2\u8DF3\u8FC7\u3002`
|
|
569
|
+
);
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
let nLoaded = 0;
|
|
573
|
+
for (const [cat, rules] of pairs) {
|
|
574
|
+
const required = EXTRA_CATEGORIES[cat];
|
|
575
|
+
if (!required) {
|
|
576
|
+
console.warn(
|
|
577
|
+
`[AgentMoss] \u8FFD\u52A0\u89C4\u5219\u6587\u4EF6 ${p} \u542B\u672A\u77E5\u7C7B\u522B\u952E "${cat}"\uFF08\u53EF\u7528\uFF1A${Object.keys(EXTRA_CATEGORIES).join(", ")}\uFF09\u2014\u2014 \u5DF2\u8DF3\u8FC7\u8BE5\u7C7B\u522B\u3002`
|
|
578
|
+
);
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
581
|
+
if (!Array.isArray(rules)) {
|
|
582
|
+
console.warn(`[AgentMoss] \u8FFD\u52A0\u89C4\u5219\u6587\u4EF6 ${p} \u7C7B\u522B ${cat} \u7684\u503C\u5E94\u662F\u6570\u7EC4 \u2014\u2014 \u5DF2\u8DF3\u8FC7\u3002`);
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
for (const r of rules) {
|
|
586
|
+
if (!r || typeof r !== "object" || !required.every((k) => r[k])) {
|
|
587
|
+
console.warn(
|
|
588
|
+
`[AgentMoss] \u8FFD\u52A0\u89C4\u5219\u6587\u4EF6 ${p} \u7C7B\u522B ${cat} \u6709\u6761\u76EE\u7F3A\u5FC5\u9700\u5B57\u6BB5 [${required.join(", ")}] \u2014\u2014 \u5DF2\u8DF3\u8FC7\u8BE5\u6761\u3002`
|
|
589
|
+
);
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
592
|
+
const rid = r && typeof r === "object" ? String(r.id || "") : "";
|
|
593
|
+
if (rid) {
|
|
594
|
+
if (seenIds.has(rid)) {
|
|
595
|
+
console.warn(
|
|
596
|
+
`[AgentMoss] \u8FFD\u52A0\u89C4\u5219 id "${rid}" \u91CD\u590D\uFF08\u5148\u89C1\u4E8E ${seenIds.get(rid)}\uFF0C\u672C\u6761\u6765\u81EA ${p}\uFF09\u2014\u2014 \u5DF2\u8DF3\u8FC7\u672C\u6761\u3002`
|
|
597
|
+
);
|
|
598
|
+
continue;
|
|
599
|
+
}
|
|
600
|
+
seenIds.set(rid, p);
|
|
601
|
+
}
|
|
602
|
+
(data[cat] = data[cat] || []).push(r);
|
|
603
|
+
nLoaded += 1;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
if (nLoaded > 0) {
|
|
607
|
+
loaded.push(p);
|
|
608
|
+
console.info(`[AgentMoss] \u5DF2\u52A0\u8F7D\u8FFD\u52A0\u89C4\u5219\u6587\u4EF6 ${p}\uFF1A${nLoaded} \u6761`);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return { data, loaded };
|
|
612
|
+
}
|
|
613
|
+
function loadExtraRules() {
|
|
614
|
+
const raw = (process.env.AGENT_MOSS_EXTRA_RULES_DIR || "").trim();
|
|
615
|
+
const dirs = extraRulesDirs();
|
|
616
|
+
let files = [];
|
|
617
|
+
for (const d of dirs) {
|
|
618
|
+
if (existsSync(d)) files.push(...listJsonFilesDeep(d));
|
|
619
|
+
else console.warn(`[AgentMoss] \u8FFD\u52A0\u89C4\u5219\u76EE\u5F55\u4E0D\u5B58\u5728\uFF0C\u5DF2\u8DF3\u8FC7: ${d}\uFF08AGENT_MOSS_EXTRA_RULES_DIR\uFF09`);
|
|
620
|
+
}
|
|
621
|
+
files = files.sort();
|
|
622
|
+
const fp = files.map((p) => {
|
|
623
|
+
try {
|
|
624
|
+
const st = statSync(p);
|
|
625
|
+
return [p, st.mtimeMs, st.size];
|
|
626
|
+
} catch {
|
|
627
|
+
return [p, -1, -1];
|
|
628
|
+
}
|
|
629
|
+
});
|
|
630
|
+
if (extraCache && extraCache.raw === raw && JSON.stringify(extraCache.fp) === JSON.stringify(fp)) {
|
|
631
|
+
return extraCache.data;
|
|
632
|
+
}
|
|
633
|
+
const { data, loaded } = scanExtraFiles(files);
|
|
634
|
+
extraCache = { raw, fp, data, loaded };
|
|
635
|
+
return data;
|
|
636
|
+
}
|
|
637
|
+
function extraRuleFiles() {
|
|
638
|
+
loadExtraRules();
|
|
639
|
+
return extraCache ? [...extraCache.loaded] : [];
|
|
640
|
+
}
|
|
641
|
+
var __filename2, __dirname2, EXPECTED_KEYS, warnedOverride, RULES_DIR_CANDS, EXTRA_CATEGORIES, EXTRA_FLAT_FIELD, extraCache;
|
|
461
642
|
var init_rules_loader = __esm({
|
|
462
643
|
"src/infra/rules-loader.ts"() {
|
|
463
644
|
"use strict";
|
|
@@ -492,6 +673,22 @@ var init_rules_loader = __esm({
|
|
|
492
673
|
];
|
|
493
674
|
return cands.filter((c) => existsSync(c));
|
|
494
675
|
})();
|
|
676
|
+
EXTRA_CATEGORIES = {
|
|
677
|
+
dangerous_commands: ["pattern"],
|
|
678
|
+
injection_detection: ["keyword", "lang"],
|
|
679
|
+
sensitive_path_access: ["path"],
|
|
680
|
+
intent_consistency: ["intent_keywords"],
|
|
681
|
+
password_modify_consent: ["pattern"],
|
|
682
|
+
user_deletion_consent: ["pattern"]
|
|
683
|
+
};
|
|
684
|
+
EXTRA_FLAT_FIELD = [
|
|
685
|
+
[["path"], "sensitive_path_access"],
|
|
686
|
+
[["keyword", "lang"], "injection_detection"],
|
|
687
|
+
[["intent_keywords"], "intent_consistency"],
|
|
688
|
+
[["pattern", "risk_level"], "dangerous_commands"],
|
|
689
|
+
[["pattern", "risk_type"], "dangerous_commands"]
|
|
690
|
+
];
|
|
691
|
+
extraCache = null;
|
|
495
692
|
}
|
|
496
693
|
});
|
|
497
694
|
|
|
@@ -708,7 +905,9 @@ var init_platform_traits = __esm({
|
|
|
708
905
|
/^ag(?:\s|$)/i,
|
|
709
906
|
/^rg(?:\s|$)/i,
|
|
710
907
|
/^awk\s+/i,
|
|
711
|
-
|
|
908
|
+
// NEW-FN-P(M03):sed -i/--in-place 是原地写,挡出白名单走 L2 read_before_write。
|
|
909
|
+
// 前瞻放最前(外层 ^sed\s+ 会消费 -i 前的空格)。与 Python profiles/linux.py 对齐。
|
|
910
|
+
/^(?!.*(?:\s|['"])-[a-zA-Z]*i[a-zA-Z]*\b)(?!.*(?:\s|['"])--in-place)sed\s+/i,
|
|
712
911
|
/^cut\s+/i,
|
|
713
912
|
/^sort(?:\s|$)/i,
|
|
714
913
|
/^uniq(?:\s|$)/i,
|
|
@@ -944,8 +1143,8 @@ function isWriteOperation(actionType, actionDetail, writeKeywords = WRITE_KEYWOR
|
|
|
944
1143
|
if (writeKeywords.some((kw) => actionType.includes(kw) || actionDetail.includes(kw))) return true;
|
|
945
1144
|
const { writeCommandRes } = getPlatformTraits(osType);
|
|
946
1145
|
if (writeCommandRes.some((rx) => rx.test(actionDetail))) return true;
|
|
947
|
-
if (
|
|
948
|
-
for (const seg of actionDetail
|
|
1146
|
+
if (SED_INPLACE_RE.test(actionDetail)) return true;
|
|
1147
|
+
for (const seg of splitCommandSegments(actionDetail)) {
|
|
949
1148
|
const trimmed = seg.trim();
|
|
950
1149
|
if (!trimmed) continue;
|
|
951
1150
|
const parts = trimmed.split(/\s+/);
|
|
@@ -1173,11 +1372,11 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
1173
1372
|
const hasRedirectWrite = />{1,2}\s*(?!&)\S/.test(detailNoQuotes);
|
|
1174
1373
|
let hasWriteSeg = hasRedirectWrite;
|
|
1175
1374
|
if (!hasWriteSeg) {
|
|
1176
|
-
for (const seg of actionDetail
|
|
1375
|
+
for (const seg of splitCommandSegments(actionDetail)) {
|
|
1177
1376
|
const trimmed = seg.trim();
|
|
1178
1377
|
if (!trimmed) continue;
|
|
1179
1378
|
const fw = trimmed.split(/\s+/)[0]?.toLowerCase() || "";
|
|
1180
|
-
if (WRITE_CMDS.has(fw) ||
|
|
1379
|
+
if (WRITE_CMDS.has(fw) || SED_INPLACE_RE.test(trimmed)) {
|
|
1181
1380
|
hasWriteSeg = true;
|
|
1182
1381
|
break;
|
|
1183
1382
|
}
|
|
@@ -1196,14 +1395,15 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
1196
1395
|
const SOURCE_ARG_CMDS = /* @__PURE__ */ new Set(["cp", "mv", "install", "rsync"]);
|
|
1197
1396
|
const REDIRECT_TARGET_RE = new RegExp(`>{1,2}\\s*(?!&)(?:'(/[^']+)'|"(/[^\\"]+)"|(/[^\\s;|&><'\\"]+))`);
|
|
1198
1397
|
const targetPaths = [];
|
|
1199
|
-
for (const seg of actionDetail
|
|
1398
|
+
for (const seg of splitCommandSegments(actionDetail)) {
|
|
1200
1399
|
const trimmed = seg.trim();
|
|
1201
1400
|
if (!trimmed) continue;
|
|
1202
1401
|
const firstWord = trimmed.split(/\s+/)[0]?.toLowerCase() || "";
|
|
1203
1402
|
if (firstWord === "cd") {
|
|
1204
1403
|
continue;
|
|
1205
1404
|
}
|
|
1206
|
-
if (
|
|
1405
|
+
if (firstWord === "sed" && SED_INPLACE_RE.test(trimmed)) {
|
|
1406
|
+
} else if (getPlatformTraits(osType).readOnlyCommands.has(firstWord)) {
|
|
1207
1407
|
const segUnquoted = trimmed.replace(new RegExp(`>{1,2}\\s*(['"])([~./][^'"]+)\\1`, "g"), (_m, _q, p) => ">" + p);
|
|
1208
1408
|
const segNoDevnull = segUnquoted.replace(DEVNULL_REDIRECT_RE, "");
|
|
1209
1409
|
const segNoQuotes = segNoDevnull.replace(/'[^']*'|"[^"]*"/g, "");
|
|
@@ -1318,7 +1518,7 @@ function checkUserDeletionConsent(actionType, actionDetail, actionHistory, osTyp
|
|
|
1318
1518
|
}
|
|
1319
1519
|
function containsPrivilegeEscalation(actionDetail) {
|
|
1320
1520
|
const ad = actionDetail.toLowerCase();
|
|
1321
|
-
for (const seg of ad
|
|
1521
|
+
for (const seg of splitCommandSegments(ad)) {
|
|
1322
1522
|
const trimmed = seg.trim();
|
|
1323
1523
|
if (!trimmed) continue;
|
|
1324
1524
|
const parts = trimmed.split(/\s+/);
|
|
@@ -1407,7 +1607,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
|
|
|
1407
1607
|
violatedLayers: ["L2.1"]
|
|
1408
1608
|
};
|
|
1409
1609
|
}
|
|
1410
|
-
var ROOT_SENSITIVE_PATHS, HOME_SENSITIVE_PATHS, CREDENTIAL_PATHS, ROOT_SENSITIVE_PATHS_WINDOWS, HOME_SENSITIVE_PATHS_WINDOWS, CREDENTIAL_PATHS_WINDOWS, ROOT_SENSITIVE_PATHS_MACOS, HOME_SENSITIVE_PATHS_MACOS, CREDENTIAL_PATHS_MACOS, ROOT_SENSITIVE_PATHS_HARMONY, HOME_SENSITIVE_PATHS_HARMONY, CREDENTIAL_PATHS_HARMONY, _scopeIndex, NON_WRITE_TOOLS, NON_WRITE_TOOL_PREFIXES, WRITE_KEYWORDS, DEVNULL_REDIRECT_RE, CREATE_KEYWORDS, PASSWORD_MODIFY_PATTERNS, USER_DELETION_PATTERNS, INTENT_DEVIATION_PATTERNS, COMBO_STAGES, COMBO_CHAINS, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW;
|
|
1610
|
+
var ROOT_SENSITIVE_PATHS, HOME_SENSITIVE_PATHS, CREDENTIAL_PATHS, ROOT_SENSITIVE_PATHS_WINDOWS, HOME_SENSITIVE_PATHS_WINDOWS, CREDENTIAL_PATHS_WINDOWS, ROOT_SENSITIVE_PATHS_MACOS, HOME_SENSITIVE_PATHS_MACOS, CREDENTIAL_PATHS_MACOS, ROOT_SENSITIVE_PATHS_HARMONY, HOME_SENSITIVE_PATHS_HARMONY, CREDENTIAL_PATHS_HARMONY, _scopeIndex, NON_WRITE_TOOLS, NON_WRITE_TOOL_PREFIXES, WRITE_KEYWORDS, SED_INPLACE_RE, DEVNULL_REDIRECT_RE, CREATE_KEYWORDS, PASSWORD_MODIFY_PATTERNS, USER_DELETION_PATTERNS, INTENT_DEVIATION_PATTERNS, COMBO_STAGES, COMBO_CHAINS, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW;
|
|
1411
1611
|
var init_logic_rules = __esm({
|
|
1412
1612
|
"src/engine/logic-rules.ts"() {
|
|
1413
1613
|
"use strict";
|
|
@@ -1416,6 +1616,7 @@ var init_logic_rules = __esm({
|
|
|
1416
1616
|
init_runtime_config_bridge();
|
|
1417
1617
|
init_rules_loader();
|
|
1418
1618
|
init_platform_traits();
|
|
1619
|
+
init_shell_split();
|
|
1419
1620
|
ROOT_SENSITIVE_PATHS = _loadSensitivePathScope("root");
|
|
1420
1621
|
HOME_SENSITIVE_PATHS = _loadSensitivePathScope("home");
|
|
1421
1622
|
CREDENTIAL_PATHS = _loadSensitivePathScope("credential").map(({ path: path6, riskLevel, desc, dirDelOnly }) => ({ path: path6, riskLevel, desc, credential: true, dirDelOnly }));
|
|
@@ -1450,6 +1651,7 @@ var init_logic_rules = __esm({
|
|
|
1450
1651
|
]);
|
|
1451
1652
|
NON_WRITE_TOOL_PREFIXES = ["memory-", "skill-manager-", "mailbox-", "app-"];
|
|
1452
1653
|
WRITE_KEYWORDS = ["write", "\u5199\u5165", "save", "\u4FDD\u5B58", "modify", "\u4FEE\u6539", "update", "\u66F4\u65B0", "delete", "\u5220\u9664", "remove", "\u79FB\u9664", "create", "\u521B\u5EFA", "append", "\u8FFD\u52A0", "truncate", "\u622A\u65AD", "overwrite", "\u8986\u76D6", "replace", "\u66FF\u6362", "edit"];
|
|
1654
|
+
SED_INPLACE_RE = /\bsed\s+(?:-\S+\s+)*(?:-[a-zA-Z]*i[a-zA-Z]*|--in-place)\b/i;
|
|
1453
1655
|
DEVNULL_REDIRECT_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/g;
|
|
1454
1656
|
CREATE_KEYWORDS = ["\u521B\u5EFA", "\u65B0\u5EFA", "create", "new file", "touch"];
|
|
1455
1657
|
PASSWORD_MODIFY_PATTERNS = (() => {
|
|
@@ -1526,7 +1728,7 @@ var init_asset_dir = __esm({
|
|
|
1526
1728
|
});
|
|
1527
1729
|
|
|
1528
1730
|
// src/engine/skill-loader.ts
|
|
1529
|
-
import { readFileSync as readFileSync3, readdirSync as
|
|
1731
|
+
import { readFileSync as readFileSync3, readdirSync as readdirSync3, existsSync as existsSync4 } from "fs";
|
|
1530
1732
|
import { resolve as resolve3, dirname as dirname2 } from "path";
|
|
1531
1733
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1532
1734
|
function loadAllSkills() {
|
|
@@ -1538,7 +1740,7 @@ function loadAllSkills() {
|
|
|
1538
1740
|
console.warn("[AgentMoss] Skills directory not found:", primary, "and fallback:", fallback);
|
|
1539
1741
|
return cachedSkills;
|
|
1540
1742
|
}
|
|
1541
|
-
const files =
|
|
1743
|
+
const files = readdirSync3(dir).filter((f) => f.endsWith(".md"));
|
|
1542
1744
|
for (const f of files) {
|
|
1543
1745
|
const name = f.replace(".md", "");
|
|
1544
1746
|
cachedSkills[name] = readFileSync3(resolve3(dir, f), "utf-8");
|
|
@@ -1717,7 +1919,7 @@ __export(runtime_config_exports, {
|
|
|
1717
1919
|
updateSkillCategoryEnabled: () => updateSkillCategoryEnabled,
|
|
1718
1920
|
updateSkillEnabled: () => updateSkillEnabled
|
|
1719
1921
|
});
|
|
1720
|
-
import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync5, mkdirSync, unlinkSync, readdirSync as
|
|
1922
|
+
import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync5, mkdirSync, unlinkSync, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
|
|
1721
1923
|
import { join as join3 } from "path";
|
|
1722
1924
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
1723
1925
|
import { dirname as dirname3 } from "path";
|
|
@@ -1763,7 +1965,7 @@ function loadRuntimeConfig() {
|
|
|
1763
1965
|
return seeded;
|
|
1764
1966
|
}
|
|
1765
1967
|
try {
|
|
1766
|
-
const st =
|
|
1968
|
+
const st = statSync2(p);
|
|
1767
1969
|
const cacheKey = `${p}|${st.mtimeMs}|${st.size}`;
|
|
1768
1970
|
if (runtimeConfigCache && runtimeConfigCache.key === cacheKey) {
|
|
1769
1971
|
return runtimeConfigCache.value;
|
|
@@ -1844,9 +2046,18 @@ function _mergeUserCustom(primary, runtime, layers, key) {
|
|
|
1844
2046
|
const extra = _userCustomRules(runtime, layers, key);
|
|
1845
2047
|
return extra.length > 0 ? [...primary, ...extra] : [...primary];
|
|
1846
2048
|
}
|
|
2049
|
+
function _mergeExtraDirRules(primary, category) {
|
|
2050
|
+
try {
|
|
2051
|
+
const extra = loadExtraRules()[category] || [];
|
|
2052
|
+
return extra.length > 0 ? [...primary, ...extra] : [...primary];
|
|
2053
|
+
} catch {
|
|
2054
|
+
return [...primary];
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
1847
2057
|
function getEnabledL1CommandPatterns(runtime, osType) {
|
|
1848
2058
|
const cat = runtime.L1_rules?.dangerous_commands || { category_enabled: true, rules: [] };
|
|
1849
2059
|
let rules = filterEnabledRules(cat.rules || [], cat.category_enabled);
|
|
2060
|
+
rules = _mergeExtraDirRules(rules, "dangerous_commands");
|
|
1850
2061
|
rules = _mergeUserCustom(rules, runtime, "L1_rules", "pattern");
|
|
1851
2062
|
if (osType) rules = _filterRulesByOs(rules, osType);
|
|
1852
2063
|
return rules.map((r) => ({
|
|
@@ -1862,6 +2073,7 @@ function getEnabledL2SensitivePaths(runtime, osType) {
|
|
|
1862
2073
|
cat = runtime.L2_rules?.sensitive_path_access || { category_enabled: true, rules: [] };
|
|
1863
2074
|
}
|
|
1864
2075
|
let rules = filterEnabledRules(cat.rules || [], cat.category_enabled);
|
|
2076
|
+
rules = _mergeExtraDirRules(rules, "sensitive_path_access");
|
|
1865
2077
|
rules = _mergeUserCustom(rules, runtime, ["L1_rules", "L2_rules"], "path");
|
|
1866
2078
|
if (osType) rules = _filterRulesByOs(rules, osType);
|
|
1867
2079
|
return rules.map((r) => {
|
|
@@ -1876,7 +2088,7 @@ function hasRuntimeConfig() {
|
|
|
1876
2088
|
const p = getRuntimeConfigPath();
|
|
1877
2089
|
let key;
|
|
1878
2090
|
try {
|
|
1879
|
-
const st =
|
|
2091
|
+
const st = statSync2(p);
|
|
1880
2092
|
key = `${p}|${st.mtimeMs}|${st.size}`;
|
|
1881
2093
|
} catch {
|
|
1882
2094
|
return false;
|
|
@@ -2106,12 +2318,12 @@ function _scenarioRulesToDefaults() {
|
|
|
2106
2318
|
if (!existsSync5(scenDir)) return out;
|
|
2107
2319
|
let entries;
|
|
2108
2320
|
try {
|
|
2109
|
-
entries =
|
|
2321
|
+
entries = readdirSync4(scenDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
2110
2322
|
} catch {
|
|
2111
2323
|
return out;
|
|
2112
2324
|
}
|
|
2113
2325
|
for (const scenarioId of entries) {
|
|
2114
|
-
const rulesFile = join3(scenDir, scenarioId, "rules.json");
|
|
2326
|
+
const rulesFile = resolveScenarioRulesFile(scenarioId) ?? join3(scenDir, scenarioId, "rules.json");
|
|
2115
2327
|
if (!existsSync5(rulesFile)) continue;
|
|
2116
2328
|
let data;
|
|
2117
2329
|
try {
|
|
@@ -2155,7 +2367,7 @@ function _agentRulesToDefaults() {
|
|
|
2155
2367
|
if (!existsSync5(agentsDir)) return out;
|
|
2156
2368
|
let entries;
|
|
2157
2369
|
try {
|
|
2158
|
-
entries =
|
|
2370
|
+
entries = readdirSync4(agentsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
2159
2371
|
} catch {
|
|
2160
2372
|
return out;
|
|
2161
2373
|
}
|
|
@@ -2493,6 +2705,7 @@ function _migrateCategoriesAcrossLayers(cfg, srcLayer, dstLayer, catNames) {
|
|
|
2493
2705
|
function getEnabledL1InjectionKeywords(runtime, osType) {
|
|
2494
2706
|
const cat = runtime.L1_rules?.injection_detection || { category_enabled: true, rules: [] };
|
|
2495
2707
|
let rules = filterEnabledRules(cat.rules || [], cat.category_enabled);
|
|
2708
|
+
rules = _mergeExtraDirRules(rules, "injection_detection");
|
|
2496
2709
|
rules = _mergeUserCustom(rules, runtime, "L1_rules", "keyword");
|
|
2497
2710
|
if (osType) rules = _filterRulesByOs(rules, osType);
|
|
2498
2711
|
return rules.map((r) => ({
|
|
@@ -2504,6 +2717,7 @@ function getEnabledL1InjectionKeywords(runtime, osType) {
|
|
|
2504
2717
|
function getEnabledL2IntentPatterns(runtime, osType) {
|
|
2505
2718
|
const cat = runtime.L2_rules?.intent_consistency || { category_enabled: true, rules: [] };
|
|
2506
2719
|
let rules = filterEnabledRules(cat.rules || [], cat.category_enabled);
|
|
2720
|
+
rules = _mergeExtraDirRules(rules, "intent_consistency");
|
|
2507
2721
|
rules = _mergeUserCustom(rules, runtime, "L2_rules", "intent_keywords");
|
|
2508
2722
|
if (osType) rules = _filterRulesByOs(rules, osType);
|
|
2509
2723
|
return rules.map((r) => ({
|
|
@@ -2514,12 +2728,22 @@ function getEnabledL2IntentPatterns(runtime, osType) {
|
|
|
2514
2728
|
}
|
|
2515
2729
|
function getEnabledL2PasswordPatterns(runtime) {
|
|
2516
2730
|
const cat = runtime.L2_rules?.password_modify_consent || { category_enabled: true, rules: [] };
|
|
2517
|
-
const rules = _mergeUserCustom(
|
|
2731
|
+
const rules = _mergeUserCustom(
|
|
2732
|
+
_mergeExtraDirRules(filterEnabledRules(cat.rules || [], cat.category_enabled), "password_modify_consent"),
|
|
2733
|
+
runtime,
|
|
2734
|
+
"L2_rules",
|
|
2735
|
+
"pattern"
|
|
2736
|
+
);
|
|
2518
2737
|
return rules.map((r) => r.pattern || "");
|
|
2519
2738
|
}
|
|
2520
2739
|
function getEnabledL2UserDeletionPatterns(runtime) {
|
|
2521
2740
|
const cat = runtime.L2_rules?.user_deletion_consent || { category_enabled: true, rules: [] };
|
|
2522
|
-
const rules = _mergeUserCustom(
|
|
2741
|
+
const rules = _mergeUserCustom(
|
|
2742
|
+
_mergeExtraDirRules(filterEnabledRules(cat.rules || [], cat.category_enabled), "user_deletion_consent"),
|
|
2743
|
+
runtime,
|
|
2744
|
+
"L2_rules",
|
|
2745
|
+
"pattern"
|
|
2746
|
+
);
|
|
2523
2747
|
return rules.map((r) => r.pattern || "");
|
|
2524
2748
|
}
|
|
2525
2749
|
function isL2CategoryEnabled(runtime, categoryKey) {
|
|
@@ -2551,28 +2775,48 @@ function getEnabledL1CommandPatternsOrDefault(runtime, osType) {
|
|
|
2551
2775
|
if (_hasRulesForOs(cat?.rules, osType)) {
|
|
2552
2776
|
return getEnabledL1CommandPatterns(runtime, osType);
|
|
2553
2777
|
}
|
|
2554
|
-
return _mergeUserCustom(
|
|
2778
|
+
return _mergeUserCustom(
|
|
2779
|
+
_mergeExtraDirRules(_filterDefaultsByOs(_l1CommandPatternsToRules(), osType), "dangerous_commands"),
|
|
2780
|
+
runtime,
|
|
2781
|
+
"L1_rules",
|
|
2782
|
+
"pattern"
|
|
2783
|
+
);
|
|
2555
2784
|
}
|
|
2556
2785
|
function getEnabledL1InjectionKeywordsOrDefault(runtime, osType) {
|
|
2557
2786
|
const cat = runtime.L1_rules?.injection_detection;
|
|
2558
2787
|
if (_hasRulesForOs(cat?.rules, osType)) {
|
|
2559
2788
|
return getEnabledL1InjectionKeywords(runtime, osType);
|
|
2560
2789
|
}
|
|
2561
|
-
return _mergeUserCustom(
|
|
2790
|
+
return _mergeUserCustom(
|
|
2791
|
+
_mergeExtraDirRules(_l1InjectionKeywordsToRules(), "injection_detection"),
|
|
2792
|
+
runtime,
|
|
2793
|
+
"L1_rules",
|
|
2794
|
+
"keyword"
|
|
2795
|
+
);
|
|
2562
2796
|
}
|
|
2563
2797
|
function getEnabledL2SensitivePathsOrDefault(runtime, osType) {
|
|
2564
2798
|
const spCat = _sensitivePathCategoryOf(runtime);
|
|
2565
2799
|
if (_hasRulesForOs(spCat?.rules, osType)) {
|
|
2566
2800
|
return getEnabledL2SensitivePaths(runtime, osType);
|
|
2567
2801
|
}
|
|
2568
|
-
return _mergeUserCustom(
|
|
2802
|
+
return _mergeUserCustom(
|
|
2803
|
+
_mergeExtraDirRules(_filterDefaultsByOs(_l2SensitivePathsToRules(), osType), "sensitive_path_access"),
|
|
2804
|
+
runtime,
|
|
2805
|
+
["L1_rules", "L2_rules"],
|
|
2806
|
+
"path"
|
|
2807
|
+
);
|
|
2569
2808
|
}
|
|
2570
2809
|
function getEnabledL2IntentPatternsOrDefault(runtime, osType) {
|
|
2571
2810
|
const cat = runtime.L2_rules?.intent_consistency;
|
|
2572
2811
|
if (_hasRulesForOs(cat?.rules, osType)) {
|
|
2573
2812
|
return getEnabledL2IntentPatterns(runtime, osType);
|
|
2574
2813
|
}
|
|
2575
|
-
return _mergeUserCustom(
|
|
2814
|
+
return _mergeUserCustom(
|
|
2815
|
+
_mergeExtraDirRules(_filterDefaultsByOs(_l2IntentPatternsToRules(), osType), "intent_consistency"),
|
|
2816
|
+
runtime,
|
|
2817
|
+
"L2_rules",
|
|
2818
|
+
"intent_keywords"
|
|
2819
|
+
);
|
|
2576
2820
|
}
|
|
2577
2821
|
function getEnabledL2PasswordPatternsOrDefault(runtime, osType) {
|
|
2578
2822
|
const cat = runtime.L2_rules?.password_modify_consent;
|
|
@@ -2968,6 +3212,7 @@ var init_runtime_config = __esm({
|
|
|
2968
3212
|
init_heuristic();
|
|
2969
3213
|
init_logic_rules();
|
|
2970
3214
|
init_skill_loader();
|
|
3215
|
+
init_rules_loader();
|
|
2971
3216
|
init_runtime_config_bridge();
|
|
2972
3217
|
__dirname4 = dirname3(fileURLToPath4(import.meta.url));
|
|
2973
3218
|
ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
|
|
@@ -3028,7 +3273,9 @@ __export(config_exports, {
|
|
|
3028
3273
|
isLLMAvailable: () => isLLMAvailable,
|
|
3029
3274
|
isLogicRulesEnabled: () => isLogicRulesEnabled,
|
|
3030
3275
|
probeLLMConnection: () => probeLLMConnection,
|
|
3031
|
-
updateLLMConfig: () => updateLLMConfig
|
|
3276
|
+
updateLLMConfig: () => updateLLMConfig,
|
|
3277
|
+
userRuleVariantPattern: () => userRuleVariantPattern,
|
|
3278
|
+
wildcardToRegExp: () => wildcardToRegExp
|
|
3032
3279
|
});
|
|
3033
3280
|
import fs2 from "fs";
|
|
3034
3281
|
import path2 from "path";
|
|
@@ -3125,6 +3372,8 @@ function loadCustomRules() {
|
|
|
3125
3372
|
const p = r?.pattern;
|
|
3126
3373
|
if (!p) continue;
|
|
3127
3374
|
rules.push({ pattern: wildcardToRegExp(p), action: "Deny", severity: "high" });
|
|
3375
|
+
const variant = userRuleVariantPattern(p);
|
|
3376
|
+
if (variant) rules.push({ pattern: wildcardToRegExp(variant), action: "Deny", severity: "high" });
|
|
3128
3377
|
}
|
|
3129
3378
|
break;
|
|
3130
3379
|
} catch {
|
|
@@ -3132,6 +3381,15 @@ function loadCustomRules() {
|
|
|
3132
3381
|
}
|
|
3133
3382
|
return rules;
|
|
3134
3383
|
}
|
|
3384
|
+
function userRuleVariantPattern(pattern) {
|
|
3385
|
+
const tokens = pattern.split(/\s+/);
|
|
3386
|
+
if (tokens.length >= 3 && tokens[1] === "*" && tokens[2] !== "*") {
|
|
3387
|
+
let variant = [tokens[0], ...tokens.slice(2)].join(" ");
|
|
3388
|
+
if (!variant.endsWith("*")) variant += " *";
|
|
3389
|
+
return variant;
|
|
3390
|
+
}
|
|
3391
|
+
return null;
|
|
3392
|
+
}
|
|
3135
3393
|
function getLLMConfig() {
|
|
3136
3394
|
return llmConfig;
|
|
3137
3395
|
}
|
|
@@ -3368,15 +3626,27 @@ function getScenarioExtensions(scenarioId) {
|
|
|
3368
3626
|
if (!scenarioId) return null;
|
|
3369
3627
|
if (cache2.has(scenarioId)) return cache2.get(scenarioId) ?? null;
|
|
3370
3628
|
let ext = null;
|
|
3629
|
+
let overrideHit = null;
|
|
3371
3630
|
try {
|
|
3372
|
-
|
|
3373
|
-
ext = getEnabledScenarioExtensions(loadRuntimeConfig(), scenarioId);
|
|
3374
|
-
}
|
|
3631
|
+
overrideHit = resolveScenarioRulesFile(scenarioId);
|
|
3375
3632
|
} catch {
|
|
3376
3633
|
}
|
|
3634
|
+
if (!overrideHit) {
|
|
3635
|
+
try {
|
|
3636
|
+
if (hasRuntimeConfig()) {
|
|
3637
|
+
ext = getEnabledScenarioExtensions(loadRuntimeConfig(), scenarioId);
|
|
3638
|
+
}
|
|
3639
|
+
} catch {
|
|
3640
|
+
}
|
|
3641
|
+
}
|
|
3377
3642
|
if (ext === null) {
|
|
3378
|
-
const
|
|
3379
|
-
const rulesFile =
|
|
3643
|
+
const builtinFile = resolve5(resolveScenariosDir(), scenarioId, "rules.json");
|
|
3644
|
+
const rulesFile = overrideHit ?? builtinFile;
|
|
3645
|
+
if (overrideHit) {
|
|
3646
|
+
console.warn(
|
|
3647
|
+
`[AgentMoss] \u26A0\uFE0F \u573A\u666F ${scenarioId} \u89C4\u5219\u88AB\u8986\u76D6\u76EE\u5F55\u8986\u76D6: ${overrideHit}\uFF08\u5185\u7F6E\u9ED8\u8BA4 ${builtinFile} \u88AB\u5FFD\u7565\uFF09\u3002\u8BE5\u6587\u4EF6\u7531\u90E8\u7F72\u4FA7\u81EA\u884C\u7EF4\u62A4\uFF0C\u5185\u7F6E\u573A\u666F\u89C4\u5219\u66F4\u65B0\u4E0D\u4F1A\u53CD\u6620\u5230\u5B83\u4E0A\u9762\u3002`
|
|
3648
|
+
);
|
|
3649
|
+
}
|
|
3380
3650
|
if (!existsSync7(rulesFile)) {
|
|
3381
3651
|
cache2.set(scenarioId, null);
|
|
3382
3652
|
return null;
|
|
@@ -3428,6 +3698,7 @@ var init_scenarios = __esm({
|
|
|
3428
3698
|
init_esm_shims();
|
|
3429
3699
|
init_agents();
|
|
3430
3700
|
init_runtime_config();
|
|
3701
|
+
init_rules_loader();
|
|
3431
3702
|
__filename5 = fileURLToPath7(import.meta.url);
|
|
3432
3703
|
__dirname6 = dirname5(__filename5);
|
|
3433
3704
|
SCENARIOS_DIR_PROD = resolve5(__dirname6, "scenarios");
|
|
@@ -3493,16 +3764,41 @@ function scanDangerousCommandPatterns(actionType, actionDetail, osType) {
|
|
|
3493
3764
|
violatedLayers: ["L1.1"]
|
|
3494
3765
|
};
|
|
3495
3766
|
}
|
|
3767
|
+
function allUrlsLoopback(actionDetail) {
|
|
3768
|
+
const hosts = actionDetail.match(new RegExp(LOOPBACK_URL_RE.source, "gi"));
|
|
3769
|
+
if (!hosts || hosts.length === 0) return false;
|
|
3770
|
+
return hosts.every((u) => {
|
|
3771
|
+
const raw = u.replace(/^https?:\/\//i, "");
|
|
3772
|
+
const host = raw.split("@").pop().split(":")[0].replace(/^\[|\]$/g, "").toLowerCase();
|
|
3773
|
+
return LOOPBACK_HOSTS.has(host) || host.startsWith("127.");
|
|
3774
|
+
});
|
|
3775
|
+
}
|
|
3776
|
+
function stripEchoParamsForUserRules(actionDetail) {
|
|
3777
|
+
return splitCommandSegments2(actionDetail).map((seg) => {
|
|
3778
|
+
const m = seg.match(/^(echo|printf)(?![a-zA-Z0-9_])\s*/i);
|
|
3779
|
+
if (!m) return seg;
|
|
3780
|
+
const head = m[1].toLowerCase();
|
|
3781
|
+
const rest = seg.slice(m[0].length);
|
|
3782
|
+
const cut = rest.search(/>+|\|/);
|
|
3783
|
+
return `${head} ` + (cut >= 0 ? rest.slice(cut) : "");
|
|
3784
|
+
}).join(" ");
|
|
3785
|
+
}
|
|
3496
3786
|
function runHeuristicCheck(input) {
|
|
3497
3787
|
const command = `${input.aNext.actionType}: ${input.aNext.actionDetail}`;
|
|
3498
3788
|
const matched = [];
|
|
3499
3789
|
let highestSeverity = "low";
|
|
3500
3790
|
let highestRiskType = "";
|
|
3501
3791
|
const userRules = getCustomRules();
|
|
3792
|
+
const atLower = input.aNext.actionType.toLowerCase();
|
|
3793
|
+
const isBashLike = ["bash", "executebash", "terminal-executebash"].includes(atLower);
|
|
3794
|
+
const urlsAllLoopback = allUrlsLoopback(input.aNext.actionDetail);
|
|
3795
|
+
const userRuleScanDetail = isBashLike ? stripEchoParamsForUserRules(input.aNext.actionDetail) : input.aNext.actionDetail;
|
|
3796
|
+
const scanCommand = `${input.aNext.actionType}: ${userRuleScanDetail}`;
|
|
3502
3797
|
for (const rule of userRules) {
|
|
3503
3798
|
try {
|
|
3504
3799
|
const re = new RegExp(rule.pattern);
|
|
3505
|
-
if (re.test(command)) {
|
|
3800
|
+
if (re.test(command) || re.test(scanCommand)) {
|
|
3801
|
+
if (urlsAllLoopback) continue;
|
|
3506
3802
|
if (rule.action === "Deny") {
|
|
3507
3803
|
return {
|
|
3508
3804
|
allowed: false,
|
|
@@ -3685,6 +3981,23 @@ function buildPathContext(actionDetail) {
|
|
|
3685
3981
|
if (!lines.length) return "";
|
|
3686
3982
|
return "## \u8DEF\u5F84\u89E3\u6790\u4E8B\u5B9E\uFF08\u8BCD\u6CD5\u5206\u6790\uFF0C\u786E\u5B9A\u6027\u7ED3\u8BBA\uFF09\n" + lines.join("\n") + "\n\n## \u8FD0\u884C\u65F6\u672A\u9A8C\u8BC1\uFF08\u4F9B\u53C2\u8003\uFF0C\u975E\u4E8B\u5B9E\uFF09\n- \u5206\u6790\u5668\u4E0D\u8BFB\u6587\u4EF6\u7CFB\u7EDF\uFF1A\u65E0\u6CD5\u786E\u8BA4\u8DEF\u5F84\u4E2D\u7684\u4E2D\u95F4\u76EE\u5F55\u662F\u5426\u4E3A\u7B26\u53F7\u94FE\u63A5\uFF1B\u82E5\u4E2D\u95F4\u6BB5\u5728\u6267\u884C\u65F6\u662F\uFF08\u6216\u88AB\u66FF\u6362\u4E3A\uFF09\u6307\u5411\u4E3B\u76EE\u5F55\u5916\u7684\u8F6F\u94FE\uFF0C\u5B9E\u9645\u64CD\u4F5C\u76EE\u6807\u53EF\u80FD\u968F\u4E4B\u6539\u53D8\u3002\u8BCD\u6CD5\u89E3\u6790\u7ED3\u679C\u57FA\u4E8E\u6807\u51C6\u8DEF\u5F84\u8BED\u4E49\uFF0C\u4E0D\u5E94\u56E0\u731C\u6D4B\u300C\u89E3\u6790\u4F9D\u8D56\u67D0\u6BB5\u662F\u5426\u5B58\u5728\u300D\u800C\u5426\u5B9A\u5B83\u3002";
|
|
3687
3983
|
}
|
|
3984
|
+
function isPurePrintCode(code) {
|
|
3985
|
+
if (code.includes("\\")) return false;
|
|
3986
|
+
if (PY_EXEC_IO_RE.test(code) || PY_BIGNUM_RE.test(code)) return false;
|
|
3987
|
+
return PY_PRINT_RE.test(code) || !/\w+\s*\(/.test(code);
|
|
3988
|
+
}
|
|
3989
|
+
function stripInlinePythonLiterals(text) {
|
|
3990
|
+
const subst = (_m, g1, g2, g3) => isPurePrintCode(g2 ?? g3 ?? "") ? "$(<<pylit>>)" : _m;
|
|
3991
|
+
const plain = (_m, g1, g2, g3) => {
|
|
3992
|
+
if (!isPurePrintCode(g2 ?? g3 ?? "")) return _m;
|
|
3993
|
+
const q = _m.trimEnd().endsWith('"') ? '"' : "'";
|
|
3994
|
+
return `${g1} -c ${q}<<pylit>>${q}`;
|
|
3995
|
+
};
|
|
3996
|
+
let out = text;
|
|
3997
|
+
out = out.replace(new RegExp(PY_CMD_SUBST_C_RE.source, PY_CMD_SUBST_C_RE.flags), subst);
|
|
3998
|
+
out = out.replace(new RegExp(PY_INLINE_C_RE.source, PY_INLINE_C_RE.flags), plain);
|
|
3999
|
+
return out;
|
|
4000
|
+
}
|
|
3688
4001
|
function stripLiteralTextSegments(actionDetail) {
|
|
3689
4002
|
const writeScriptExec = />\s*[/~]\S*\.(sh|py|pl|rb)\b/.test(actionDetail) && /(?:&&|\|\||;|\n|\|)\s*(?:bash|sh|zsh|dash|python|perl|ruby|node)/.test(actionDetail);
|
|
3690
4003
|
const pipeToInterpreter = /\|\s*(?:bash|sh|zsh|dash|python|perl|ruby|node|crontab|at)\b/.test(actionDetail);
|
|
@@ -3695,13 +4008,14 @@ function stripLiteralTextSegments(actionDetail) {
|
|
|
3695
4008
|
for (const seg of splitCommandSegments2(actionDetail)) {
|
|
3696
4009
|
const fw = (seg.trim().split(/\s+/)[0] || "").toLowerCase();
|
|
3697
4010
|
if (fw === "echo" || fw === "printf") {
|
|
3698
|
-
const
|
|
4011
|
+
const segPy = stripInlinePythonLiterals(seg);
|
|
4012
|
+
const segUnquotedRedirect = segPy.replace(
|
|
3699
4013
|
/>{1,2}\s*(['"])([~./][^'"]+)\1/g,
|
|
3700
4014
|
(_m, _q, p) => ">" + p
|
|
3701
4015
|
);
|
|
3702
4016
|
resultParts.push(segUnquotedRedirect.replace(/'[^']*'|"[^"]*"/g, ""));
|
|
3703
4017
|
} else {
|
|
3704
|
-
resultParts.push(seg);
|
|
4018
|
+
resultParts.push(stripInlinePythonLiterals(seg));
|
|
3705
4019
|
}
|
|
3706
4020
|
}
|
|
3707
4021
|
return resultParts.join("");
|
|
@@ -4225,7 +4539,7 @@ function checkLateralMovement(actionDetail) {
|
|
|
4225
4539
|
}
|
|
4226
4540
|
return null;
|
|
4227
4541
|
}
|
|
4228
|
-
var INJECTION_KEYWORDS, DANGEROUS_PATTERNS2, DANGEROUS_PATTERNS_WINDOWS, DANGEROUS_PATTERNS_MACOS, DANGEROUS_PATTERNS_HARMONY, PATH_CONTEXT_TRIGGER_RE, HOME_PREFIX_RE, SENSITIVE_EXCLUDED_PREFIXES, SEARCH_ACTION_PREFIXES, HOME_TARGET_RE, HOME_ASSIGN_RE;
|
|
4542
|
+
var INJECTION_KEYWORDS, DANGEROUS_PATTERNS2, DANGEROUS_PATTERNS_WINDOWS, DANGEROUS_PATTERNS_MACOS, DANGEROUS_PATTERNS_HARMONY, LOOPBACK_URL_RE, LOOPBACK_HOSTS, PATH_CONTEXT_TRIGGER_RE, HOME_PREFIX_RE, PY_EXEC_IO_RE, PY_PRINT_RE, PY_BIGNUM_RE, PY_INLINE_C_RE, PY_CMD_SUBST_C_RE, SENSITIVE_EXCLUDED_PREFIXES, SEARCH_ACTION_PREFIXES, HOME_TARGET_RE, HOME_ASSIGN_RE;
|
|
4229
4543
|
var init_heuristic = __esm({
|
|
4230
4544
|
"src/engine/heuristic.ts"() {
|
|
4231
4545
|
"use strict";
|
|
@@ -4249,8 +4563,15 @@ var init_heuristic = __esm({
|
|
|
4249
4563
|
DANGEROUS_PATTERNS_WINDOWS = _loadDangerousPatterns("windows");
|
|
4250
4564
|
DANGEROUS_PATTERNS_MACOS = _loadDangerousPatterns("macos");
|
|
4251
4565
|
DANGEROUS_PATTERNS_HARMONY = _loadDangerousPatterns("harmony");
|
|
4566
|
+
LOOPBACK_URL_RE = /https?:\/\/([^\/\s;'"|&)]+)/i;
|
|
4567
|
+
LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "::1", "0.0.0.0", "[::1]"]);
|
|
4252
4568
|
PATH_CONTEXT_TRIGGER_RE = /(?:^|[;&|]\s*)(?:rm|rmdir|unlink|ln|mv|cp|dd|truncate|shred|chmod|chown)\b/i;
|
|
4253
4569
|
HOME_PREFIX_RE = /(?:~|~(?=\/)|\$HOME\b|\$\{HOME\}|\/home\/[a-z0-9_.-]+)/gi;
|
|
4570
|
+
PY_EXEC_IO_RE = /os\.system|os\.popen|subprocess|eval\s*\(|exec\s*\(|__import__|open\s*\(|socket|urllib|requests\.|shutil|os\.(?:remove|unlink|rename|makedirs?|listdir|scandir|walk|chmod|chown|kill|environ)|iterdir|glob\s*\(|path\(/i;
|
|
4571
|
+
PY_PRINT_RE = /\b(?:print|sys\.stdout\.write)\s*\(/;
|
|
4572
|
+
PY_BIGNUM_RE = /\d{6,}/;
|
|
4573
|
+
PY_INLINE_C_RE = /(python(?:3(?:\.\d+)?)?|perl5?|ruby|node|php|lua)\s+(?:-[a-zA-Z]+\s+)*-c\s+(?:"([^"]*)"|'([^']*)')/i;
|
|
4574
|
+
PY_CMD_SUBST_C_RE = /\$\(\s*(python(?:3(?:\.\d+)?)?|perl5?|ruby|node|php|lua)\s+(?:-[a-zA-Z]+\s+)*-c\s+(?:"([^"]*)"'|'([^']*)')\s*\)/i;
|
|
4254
4575
|
SENSITIVE_EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
4255
4576
|
SEARCH_ACTION_PREFIXES = ["search-", "web-", "browser-", "query-", "websearch", "web_search"];
|
|
4256
4577
|
HOME_TARGET_RE = /(?:^|[\s="'($])(?:~|(?:\$home|\$\{home\}))(?:\/(?:\.\*|\*|\.)?)?(?=[\s"';}|&)]|$)/i;
|
|
@@ -6026,7 +6347,7 @@ var init_tracker = __esm({
|
|
|
6026
6347
|
});
|
|
6027
6348
|
|
|
6028
6349
|
// src/engine/script-content-analyzer.ts
|
|
6029
|
-
import { statSync as
|
|
6350
|
+
import { statSync as statSync4, existsSync as existsSync10 } from "fs";
|
|
6030
6351
|
import { resolve as resolve9, join as join6, isAbsolute as isAbsolute2 } from "path";
|
|
6031
6352
|
import { homedir as homedir3 } from "os";
|
|
6032
6353
|
function emptyChain() {
|
|
@@ -6140,7 +6461,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
6140
6461
|
try {
|
|
6141
6462
|
const resolved = resolvePath2(tok.replace(/^['"]|['"]$/g, ""));
|
|
6142
6463
|
if (existsSync10(resolved)) {
|
|
6143
|
-
const stat =
|
|
6464
|
+
const stat = statSync4(resolved);
|
|
6144
6465
|
if (stat.isFile()) return resolved;
|
|
6145
6466
|
}
|
|
6146
6467
|
} catch {
|
|
@@ -6154,7 +6475,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
6154
6475
|
try {
|
|
6155
6476
|
const resolved = resolvePath2(p);
|
|
6156
6477
|
if (existsSync10(resolved)) {
|
|
6157
|
-
const stat =
|
|
6478
|
+
const stat = statSync4(resolved);
|
|
6158
6479
|
if (stat.isFile()) return resolved;
|
|
6159
6480
|
}
|
|
6160
6481
|
} catch {
|
|
@@ -6170,7 +6491,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
6170
6491
|
try {
|
|
6171
6492
|
resolved = resolvePath2(p);
|
|
6172
6493
|
if (existsSync10(resolved)) {
|
|
6173
|
-
const stat =
|
|
6494
|
+
const stat = statSync4(resolved);
|
|
6174
6495
|
if (stat.isFile()) return resolved;
|
|
6175
6496
|
}
|
|
6176
6497
|
} catch {
|
|
@@ -10345,10 +10666,22 @@ function logDeploymentSelfCheck() {
|
|
|
10345
10666
|
try {
|
|
10346
10667
|
const overrides = overriddenRuleFiles();
|
|
10347
10668
|
if (overrides.length > 0) {
|
|
10348
|
-
console.error(`\u26A0\uFE0F \u68C0\u6D4B\u5230
|
|
10669
|
+
console.error(`\u26A0\uFE0F \u68C0\u6D4B\u5230 ${overrides.length} \u4E2A\u89C4\u5219\u8986\u76D6\u6587\u4EF6\uFF0C**\u6574\u4EFD\u66FF\u6362**\u4E86\u5185\u7F6E\u89C4\u5219\uFF1A`);
|
|
10349
10670
|
for (const p of overrides) console.error(` ${p}`);
|
|
10350
10671
|
console.error(
|
|
10351
|
-
" \u8FD9\u4E9B\u6587\u4EF6\u7531\u90E8\u7F72\u4FA7\u81EA\u884C\u7EF4\u62A4\uFF0C**\u5185\u7F6E\u89C4\u5219\u66F4\u65B0\u4E0D\u4F1A\u53CD\u6620\u5230\u5B83\u4EEC\u4E0A\u9762**\uFF1B\u5220\u9664\u5BF9\u5E94\u6587\u4EF6\u5373\u6062\u590D\u5185\u7F6E\u3002\u82E5\u53EA\u60F3\u9010\u6761\u542F\u505C/\u589E\u5220\uFF0C\u8BF7\u6539\u7528 runtime config\uFF08Console\uFF09\u3002"
|
|
10672
|
+
" \u8FD9\u4E9B\u6587\u4EF6\u7531\u90E8\u7F72\u4FA7\u81EA\u884C\u7EF4\u62A4\uFF0C**\u5185\u7F6E\u89C4\u5219\u66F4\u65B0\u4E0D\u4F1A\u53CD\u6620\u5230\u5B83\u4EEC\u4E0A\u9762**\uFF1B\u5220\u9664\u5BF9\u5E94\u6587\u4EF6\uFF08\u6216\u6E05\u7A7A AGENT_MOSS_OVERRIDE_RULES_DIR\uFF09\u5373\u6062\u590D\u5185\u7F6E\u3002\u82E5\u53EA\u60F3\u9010\u6761\u542F\u505C/\u589E\u5220\uFF0C\u8BF7\u6539\u7528 runtime config\uFF08Console\uFF09\u3002"
|
|
10673
|
+
);
|
|
10674
|
+
}
|
|
10675
|
+
} catch {
|
|
10676
|
+
}
|
|
10677
|
+
try {
|
|
10678
|
+
const extras = extraRuleFiles();
|
|
10679
|
+
if (extras.length > 0) {
|
|
10680
|
+
const cats = loadExtraRules();
|
|
10681
|
+
const total = Object.values(cats).reduce((n, v) => n + v.length, 0);
|
|
10682
|
+
const catSummary = Object.entries(cats).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}\xD7${v.length}`).join(", ");
|
|
10683
|
+
console.log(
|
|
10684
|
+
`\u5DF2\u4ECE\u8FFD\u52A0\u76EE\u5F55 ${extraRulesDirs().join(", ")} \u52A0\u8F7D ${extras.length} \u4E2A\u89C4\u5219\u6587\u4EF6\uFF08\u5171 ${total} \u6761${catSummary ? `\uFF0C\u7C7B\u522B\uFF1A${catSummary}` : ""}\uFF09\u3002\u8FFD\u52A0\u8BED\u4E49\uFF1A\u5185\u7F6E\u89C4\u5219\u5168\u90E8\u4FDD\u7559\uFF0C\u8FD9\u4E9B\u89C4\u5219\u6302\u5728\u5176\u540E\u4E00\u8D77\u5224\u5B9A\u3002`
|
|
10352
10685
|
);
|
|
10353
10686
|
}
|
|
10354
10687
|
} catch {
|