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