@gendigital/sage-openclaw 0.5.4 → 0.6.0
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/README.md +3 -3
- package/dist/index.cjs +186 -68
- package/dist/index.cjs.map +4 -4
- package/openclaw.plugin.json +1 -1
- package/package.json +10 -10
- package/resources/threats/commands.yaml +1 -1
- package/resources/threats/mitre.yaml +2 -2
- package/resources/threats/self-defense.yaml +1 -1
package/README.md
CHANGED
|
@@ -33,9 +33,9 @@ Sage works out of the box with no configuration. Optional config file at `~/.sag
|
|
|
33
33
|
|
|
34
34
|
## Links
|
|
35
35
|
|
|
36
|
-
- [Source & documentation](https://github.com/
|
|
37
|
-
- [OpenClaw platform guide](https://github.com/
|
|
38
|
-
- [Privacy policy](https://github.com/
|
|
36
|
+
- [Source & documentation](https://github.com/gendigitalinc/sage)
|
|
37
|
+
- [OpenClaw platform guide](https://github.com/gendigitalinc/sage/blob/main/docs/platform-guides/openclaw.md)
|
|
38
|
+
- [Privacy policy](https://github.com/gendigitalinc/sage/blob/main/docs/privacy.md)
|
|
39
39
|
|
|
40
40
|
## License
|
|
41
41
|
|
package/dist/index.cjs
CHANGED
|
@@ -6868,14 +6868,14 @@ var require_parser = __commonJS({
|
|
|
6868
6868
|
case "scalar":
|
|
6869
6869
|
case "single-quoted-scalar":
|
|
6870
6870
|
case "double-quoted-scalar": {
|
|
6871
|
-
const
|
|
6871
|
+
const fs2 = this.flowScalar(this.type);
|
|
6872
6872
|
if (atNextItem || it.value) {
|
|
6873
|
-
map.items.push({ start, key:
|
|
6873
|
+
map.items.push({ start, key: fs2, sep: [] });
|
|
6874
6874
|
this.onKeyLine = true;
|
|
6875
6875
|
} else if (it.sep) {
|
|
6876
|
-
this.stack.push(
|
|
6876
|
+
this.stack.push(fs2);
|
|
6877
6877
|
} else {
|
|
6878
|
-
Object.assign(it, { key:
|
|
6878
|
+
Object.assign(it, { key: fs2, sep: [] });
|
|
6879
6879
|
this.onKeyLine = true;
|
|
6880
6880
|
}
|
|
6881
6881
|
return;
|
|
@@ -7003,13 +7003,13 @@ var require_parser = __commonJS({
|
|
|
7003
7003
|
case "scalar":
|
|
7004
7004
|
case "single-quoted-scalar":
|
|
7005
7005
|
case "double-quoted-scalar": {
|
|
7006
|
-
const
|
|
7006
|
+
const fs2 = this.flowScalar(this.type);
|
|
7007
7007
|
if (!it || it.value)
|
|
7008
|
-
fc.items.push({ start: [], key:
|
|
7008
|
+
fc.items.push({ start: [], key: fs2, sep: [] });
|
|
7009
7009
|
else if (it.sep)
|
|
7010
|
-
this.stack.push(
|
|
7010
|
+
this.stack.push(fs2);
|
|
7011
7011
|
else
|
|
7012
|
-
Object.assign(it, { key:
|
|
7012
|
+
Object.assign(it, { key: fs2, sep: [] });
|
|
7013
7013
|
return;
|
|
7014
7014
|
}
|
|
7015
7015
|
case "flow-map-end":
|
|
@@ -7330,6 +7330,7 @@ var import_node_path2 = require("node:path");
|
|
|
7330
7330
|
|
|
7331
7331
|
// ../core/dist/file-utils.js
|
|
7332
7332
|
var import_node_crypto = require("node:crypto");
|
|
7333
|
+
var fs = __toESM(require("node:fs"), 1);
|
|
7333
7334
|
var fsPromises = __toESM(require("node:fs/promises"), 1);
|
|
7334
7335
|
var import_node_path = require("node:path");
|
|
7335
7336
|
var name1 = "read";
|
|
@@ -7337,6 +7338,9 @@ var name2 = "File";
|
|
|
7337
7338
|
function getFileContent(path, encoding = "utf-8") {
|
|
7338
7339
|
return fsPromises[name1 + name2](path, encoding);
|
|
7339
7340
|
}
|
|
7341
|
+
function getFileContentSync(path, encoding = "utf-8") {
|
|
7342
|
+
return fs[`${name1 + name2}Sync`](path, encoding);
|
|
7343
|
+
}
|
|
7340
7344
|
function getFileContentRaw(path) {
|
|
7341
7345
|
return fsPromises[name1 + name2](path);
|
|
7342
7346
|
}
|
|
@@ -11500,10 +11504,6 @@ var ConfigSchema = external_exports.object({
|
|
|
11500
11504
|
|
|
11501
11505
|
// ../core/dist/config.js
|
|
11502
11506
|
var SAGE_DIR = "~/.sage";
|
|
11503
|
-
function currentHomeDir() {
|
|
11504
|
-
const envHome = process.env.HOME?.trim();
|
|
11505
|
-
return envHome && envHome.length > 0 ? envHome : (0, import_node_os.homedir)();
|
|
11506
|
-
}
|
|
11507
11507
|
function resolvedSageDir() {
|
|
11508
11508
|
return resolvePath(SAGE_DIR);
|
|
11509
11509
|
}
|
|
@@ -11521,7 +11521,7 @@ function defaultAuditPath() {
|
|
|
11521
11521
|
}
|
|
11522
11522
|
function resolvePath(pathStr) {
|
|
11523
11523
|
if (pathStr.startsWith("~/") || pathStr === "~") {
|
|
11524
|
-
return (0, import_node_path2.join)(
|
|
11524
|
+
return (0, import_node_path2.join)((0, import_node_os.homedir)(), pathStr.slice(1));
|
|
11525
11525
|
}
|
|
11526
11526
|
return pathStr;
|
|
11527
11527
|
}
|
|
@@ -11951,10 +11951,12 @@ var VerdictCache = class {
|
|
|
11951
11951
|
path;
|
|
11952
11952
|
config;
|
|
11953
11953
|
logger;
|
|
11954
|
-
|
|
11954
|
+
version;
|
|
11955
|
+
constructor(config, logger = nullLogger, version) {
|
|
11955
11956
|
this.config = config;
|
|
11956
11957
|
this.logger = logger;
|
|
11957
11958
|
this.path = resolvePath(config.path);
|
|
11959
|
+
this.version = version;
|
|
11958
11960
|
}
|
|
11959
11961
|
async load() {
|
|
11960
11962
|
if (!this.config.enabled)
|
|
@@ -11976,12 +11978,8 @@ var VerdictCache = class {
|
|
|
11976
11978
|
return null;
|
|
11977
11979
|
const key = normalizeUrl(url);
|
|
11978
11980
|
const entry = this.store.urls[key];
|
|
11979
|
-
if (!entry)
|
|
11981
|
+
if (!entry || this.isStale(entry))
|
|
11980
11982
|
return null;
|
|
11981
|
-
if (new Date(entry.expiresAt).getTime() <= Date.now()) {
|
|
11982
|
-
delete this.store.urls[key];
|
|
11983
|
-
return null;
|
|
11984
|
-
}
|
|
11985
11983
|
return {
|
|
11986
11984
|
verdict: entry.verdict,
|
|
11987
11985
|
severity: entry.severity,
|
|
@@ -11999,19 +11997,16 @@ var VerdictCache = class {
|
|
|
11999
11997
|
this.store.urls[key] = {
|
|
12000
11998
|
...verdict,
|
|
12001
11999
|
checkedAt: now.toISOString(),
|
|
12002
|
-
expiresAt: expiresAt.toISOString()
|
|
12000
|
+
expiresAt: expiresAt.toISOString(),
|
|
12001
|
+
sageVersion: this.version
|
|
12003
12002
|
};
|
|
12004
12003
|
}
|
|
12005
12004
|
getCommand(commandHash) {
|
|
12006
12005
|
if (!this.config.enabled)
|
|
12007
12006
|
return null;
|
|
12008
12007
|
const entry = this.store.commands[commandHash];
|
|
12009
|
-
if (!entry)
|
|
12008
|
+
if (!entry || this.isStale(entry))
|
|
12010
12009
|
return null;
|
|
12011
|
-
if (new Date(entry.expiresAt).getTime() <= Date.now()) {
|
|
12012
|
-
delete this.store.commands[commandHash];
|
|
12013
|
-
return null;
|
|
12014
|
-
}
|
|
12015
12010
|
return {
|
|
12016
12011
|
verdict: entry.verdict,
|
|
12017
12012
|
severity: entry.severity,
|
|
@@ -12026,19 +12021,16 @@ var VerdictCache = class {
|
|
|
12026
12021
|
this.store.commands[commandHash] = {
|
|
12027
12022
|
...verdict,
|
|
12028
12023
|
checkedAt: now.toISOString(),
|
|
12029
|
-
expiresAt: FAR_FUTURE
|
|
12024
|
+
expiresAt: FAR_FUTURE,
|
|
12025
|
+
sageVersion: this.version
|
|
12030
12026
|
};
|
|
12031
12027
|
}
|
|
12032
12028
|
getPackage(key) {
|
|
12033
12029
|
if (!this.config.enabled)
|
|
12034
12030
|
return null;
|
|
12035
12031
|
const entry = this.store.packages[key];
|
|
12036
|
-
if (!entry)
|
|
12032
|
+
if (!entry || this.isStale(entry))
|
|
12037
12033
|
return null;
|
|
12038
|
-
if (new Date(entry.expiresAt).getTime() <= Date.now()) {
|
|
12039
|
-
delete this.store.packages[key];
|
|
12040
|
-
return null;
|
|
12041
|
-
}
|
|
12042
12034
|
return {
|
|
12043
12035
|
verdict: entry.verdict,
|
|
12044
12036
|
severity: entry.severity,
|
|
@@ -12055,9 +12047,17 @@ var VerdictCache = class {
|
|
|
12055
12047
|
this.store.packages[key] = {
|
|
12056
12048
|
...verdict,
|
|
12057
12049
|
checkedAt: now.toISOString(),
|
|
12058
|
-
expiresAt: expiresAt.toISOString()
|
|
12050
|
+
expiresAt: expiresAt.toISOString(),
|
|
12051
|
+
sageVersion: this.version
|
|
12059
12052
|
};
|
|
12060
12053
|
}
|
|
12054
|
+
isStale(entry) {
|
|
12055
|
+
if (new Date(entry.expiresAt).getTime() <= Date.now())
|
|
12056
|
+
return true;
|
|
12057
|
+
if (this.version && this.version !== "dev" && entry.sageVersion !== this.version)
|
|
12058
|
+
return true;
|
|
12059
|
+
return false;
|
|
12060
|
+
}
|
|
12061
12061
|
computePackageTtl(verdict, packageAgeDays) {
|
|
12062
12062
|
const isFresh = packageAgeDays !== null && packageAgeDays < 7;
|
|
12063
12063
|
switch (verdict) {
|
|
@@ -12073,15 +12073,37 @@ var VerdictCache = class {
|
|
|
12073
12073
|
if (!this.config.enabled)
|
|
12074
12074
|
return;
|
|
12075
12075
|
try {
|
|
12076
|
+
this.pruneStaleEntries();
|
|
12076
12077
|
await atomicWriteJson(this.path, this.store);
|
|
12077
12078
|
} catch (e) {
|
|
12078
12079
|
this.logger.warn(`Failed to save cache to ${this.path}`, { error: String(e) });
|
|
12079
12080
|
}
|
|
12080
12081
|
}
|
|
12082
|
+
pruneStaleEntries() {
|
|
12083
|
+
for (const [key, entry] of Object.entries(this.store.urls)) {
|
|
12084
|
+
if (this.isStale(entry))
|
|
12085
|
+
delete this.store.urls[key];
|
|
12086
|
+
}
|
|
12087
|
+
for (const [key, entry] of Object.entries(this.store.commands)) {
|
|
12088
|
+
if (this.isStale(entry))
|
|
12089
|
+
delete this.store.commands[key];
|
|
12090
|
+
}
|
|
12091
|
+
for (const [key, entry] of Object.entries(this.store.packages)) {
|
|
12092
|
+
if (this.isStale(entry))
|
|
12093
|
+
delete this.store.packages[key];
|
|
12094
|
+
}
|
|
12095
|
+
}
|
|
12081
12096
|
};
|
|
12082
12097
|
|
|
12098
|
+
// ../core/dist/clients/amsi-spawn.js
|
|
12099
|
+
var chldproc1 = "node:child_";
|
|
12100
|
+
var chldproc2 = "process";
|
|
12101
|
+
var chldproc = require(chldproc1 + chldproc2);
|
|
12102
|
+
function spawn(command, args, options) {
|
|
12103
|
+
return chldproc.spawn(command, args, options);
|
|
12104
|
+
}
|
|
12105
|
+
|
|
12083
12106
|
// ../core/dist/clients/amsi.js
|
|
12084
|
-
var import_node_child_process = require("node:child_process");
|
|
12085
12107
|
var AMSI_RESULT_DETECTED = 32768;
|
|
12086
12108
|
var AMSI_RESULT_BLOCKED_BY_ADMIN_START = 16384;
|
|
12087
12109
|
var MAX_SCAN_LENGTH = 1048576;
|
|
@@ -12089,7 +12111,7 @@ var PS_TIMEOUT = 15e3;
|
|
|
12089
12111
|
function isWSL() {
|
|
12090
12112
|
if (process.platform !== "linux")
|
|
12091
12113
|
return false;
|
|
12092
|
-
return !!process
|
|
12114
|
+
return !!process["env"]["WSL_DISTRO_NAME"];
|
|
12093
12115
|
}
|
|
12094
12116
|
function isAmsiSupported() {
|
|
12095
12117
|
return process.platform === "win32" || isWSL();
|
|
@@ -12340,7 +12362,7 @@ var PersistentPowershellAmsiBackend = class {
|
|
|
12340
12362
|
}
|
|
12341
12363
|
async init() {
|
|
12342
12364
|
try {
|
|
12343
|
-
this.process =
|
|
12365
|
+
this.process = spawn("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", PS_PERSISTENT_SCRIPT], {
|
|
12344
12366
|
stdio: ["pipe", "pipe", "pipe"],
|
|
12345
12367
|
windowsHide: true
|
|
12346
12368
|
});
|
|
@@ -12464,7 +12486,7 @@ var WslPowershellAmsiBackend = class {
|
|
|
12464
12486
|
const truncated = content.length > MAX_SCAN_LENGTH ? content.slice(0, MAX_SCAN_LENGTH) : content;
|
|
12465
12487
|
return new Promise((resolve3) => {
|
|
12466
12488
|
try {
|
|
12467
|
-
const ps =
|
|
12489
|
+
const ps = spawn("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", PS_ONESHOT_SCRIPT], { stdio: ["pipe", "pipe", "pipe"] });
|
|
12468
12490
|
let stdout = "";
|
|
12469
12491
|
const timer = setTimeout(() => {
|
|
12470
12492
|
this.logger.warn("AMSI: PowerShell one-shot timeout", { contentName });
|
|
@@ -12556,7 +12578,23 @@ var AmsiClient = class {
|
|
|
12556
12578
|
};
|
|
12557
12579
|
|
|
12558
12580
|
// ../core/dist/version.js
|
|
12559
|
-
var
|
|
12581
|
+
var import_node_fs = require("node:fs");
|
|
12582
|
+
var import_node_path5 = require("node:path");
|
|
12583
|
+
var import_node_url = require("node:url");
|
|
12584
|
+
var import_meta = {};
|
|
12585
|
+
function resolveVersion() {
|
|
12586
|
+
if (true)
|
|
12587
|
+
return "0.6.0";
|
|
12588
|
+
try {
|
|
12589
|
+
const pkgPath = (0, import_node_path5.join)((0, import_node_path5.dirname)((0, import_node_url.fileURLToPath)(import_meta.url)), "..", "package.json");
|
|
12590
|
+
const pkg = JSON.parse((0, import_node_fs.readFileSync)(pkgPath, "utf-8"));
|
|
12591
|
+
if (typeof pkg.version === "string")
|
|
12592
|
+
return pkg.version;
|
|
12593
|
+
} catch {
|
|
12594
|
+
}
|
|
12595
|
+
return "dev";
|
|
12596
|
+
}
|
|
12597
|
+
var VERSION = resolveVersion();
|
|
12560
12598
|
|
|
12561
12599
|
// ../core/dist/clients/file-check.js
|
|
12562
12600
|
var DEFAULT_TIMEOUT = 5;
|
|
@@ -13234,7 +13272,7 @@ function extractFromEdit(toolInput) {
|
|
|
13234
13272
|
|
|
13235
13273
|
// ../core/dist/trusted-domains.js
|
|
13236
13274
|
var import_promises2 = require("node:fs/promises");
|
|
13237
|
-
var
|
|
13275
|
+
var import_node_path6 = require("node:path");
|
|
13238
13276
|
var import_yaml = __toESM(require_dist(), 1);
|
|
13239
13277
|
async function loadTrustedDomains(allowlistsDir, logger = nullLogger) {
|
|
13240
13278
|
let files;
|
|
@@ -13246,7 +13284,7 @@ async function loadTrustedDomains(allowlistsDir, logger = nullLogger) {
|
|
|
13246
13284
|
}
|
|
13247
13285
|
const domains = [];
|
|
13248
13286
|
for (const filename of files) {
|
|
13249
|
-
const filePath = (0,
|
|
13287
|
+
const filePath = (0, import_node_path6.join)(allowlistsDir, filename);
|
|
13250
13288
|
let content;
|
|
13251
13289
|
try {
|
|
13252
13290
|
content = await getFileContent(filePath);
|
|
@@ -13824,7 +13862,7 @@ function extractFromRequirementsTxt(content) {
|
|
|
13824
13862
|
|
|
13825
13863
|
// ../core/dist/threat-loader.js
|
|
13826
13864
|
var import_promises3 = require("node:fs/promises");
|
|
13827
|
-
var
|
|
13865
|
+
var import_node_path7 = require("node:path");
|
|
13828
13866
|
var import_yaml2 = __toESM(require_dist(), 1);
|
|
13829
13867
|
var REQUIRED_FIELDS = /* @__PURE__ */ new Set([
|
|
13830
13868
|
"id",
|
|
@@ -13862,7 +13900,7 @@ async function loadThreats(threatDir, logger = nullLogger) {
|
|
|
13862
13900
|
return threats;
|
|
13863
13901
|
}
|
|
13864
13902
|
for (const filename of files) {
|
|
13865
|
-
const filePath = (0,
|
|
13903
|
+
const filePath = (0, import_node_path7.join)(threatDir, filename);
|
|
13866
13904
|
let content;
|
|
13867
13905
|
try {
|
|
13868
13906
|
content = await getFileContent(filePath);
|
|
@@ -13958,7 +13996,7 @@ async function evaluateToolCall(request, context) {
|
|
|
13958
13996
|
}
|
|
13959
13997
|
let cache = null;
|
|
13960
13998
|
try {
|
|
13961
|
-
cache = new VerdictCache(config.cache, logger);
|
|
13999
|
+
cache = new VerdictCache(config.cache, logger, VERSION);
|
|
13962
14000
|
await cache.load();
|
|
13963
14001
|
} catch {
|
|
13964
14002
|
cache = null;
|
|
@@ -14189,7 +14227,7 @@ function separatorLine(headerLength) {
|
|
|
14189
14227
|
return "\u2501".repeat(headerLength);
|
|
14190
14228
|
}
|
|
14191
14229
|
function formatUpdateNotice(result) {
|
|
14192
|
-
return `\u2B06\uFE0F Update available: v${result.currentVersion} \u2192 v${result.latestVersion} (https://github.com/
|
|
14230
|
+
return `\u2B06\uFE0F Update available: v${result.currentVersion} \u2192 v${result.latestVersion} (https://github.com/gendigitalinc/sage)`;
|
|
14193
14231
|
}
|
|
14194
14232
|
function formatStartupClean(version, versionCheck) {
|
|
14195
14233
|
const base = `\u{1F6E1}\uFE0F Sage v${version} by Gen Digital \u2705 No threats found`;
|
|
@@ -14377,13 +14415,13 @@ async function removeFromAllowlist(type, value, configPath, logger = nullLogger)
|
|
|
14377
14415
|
// ../core/dist/installation-id.js
|
|
14378
14416
|
var import_node_crypto4 = require("node:crypto");
|
|
14379
14417
|
var import_promises4 = require("node:fs/promises");
|
|
14380
|
-
var
|
|
14418
|
+
var import_node_path8 = require("node:path");
|
|
14381
14419
|
async function getInstallationId(sageDirPath) {
|
|
14382
14420
|
const sageDir = sageDirPath ?? resolvePath("~/.sage");
|
|
14383
|
-
const idPath = (0,
|
|
14421
|
+
const idPath = (0, import_node_path8.join)(sageDir, "installation-id");
|
|
14384
14422
|
let fileExists = false;
|
|
14385
14423
|
try {
|
|
14386
|
-
const existing = await (
|
|
14424
|
+
const existing = await getFileContent(idPath, "utf-8");
|
|
14387
14425
|
const trimmed = existing.trim();
|
|
14388
14426
|
if (trimmed.length > 0)
|
|
14389
14427
|
return trimmed;
|
|
@@ -14398,7 +14436,7 @@ async function getInstallationId(sageDirPath) {
|
|
|
14398
14436
|
} catch (err) {
|
|
14399
14437
|
if (err.code === "EEXIST") {
|
|
14400
14438
|
try {
|
|
14401
|
-
const existing = await (
|
|
14439
|
+
const existing = await getFileContent(idPath, "utf-8");
|
|
14402
14440
|
return existing.trim() || void 0;
|
|
14403
14441
|
} catch {
|
|
14404
14442
|
return void 0;
|
|
@@ -14412,8 +14450,8 @@ async function getInstallationId(sageDirPath) {
|
|
|
14412
14450
|
var import_node_crypto5 = require("node:crypto");
|
|
14413
14451
|
var import_promises5 = require("node:fs/promises");
|
|
14414
14452
|
var import_node_os3 = require("node:os");
|
|
14415
|
-
var
|
|
14416
|
-
var DEFAULT_CACHE_PATH = (0,
|
|
14453
|
+
var import_node_path9 = require("node:path");
|
|
14454
|
+
var DEFAULT_CACHE_PATH = (0, import_node_path9.join)((0, import_node_os3.homedir)(), ".sage", "plugin_scan_cache.json");
|
|
14417
14455
|
var CACHE_TTL_DAYS = 7;
|
|
14418
14456
|
function cacheKey(pluginKey, version, lastUpdated) {
|
|
14419
14457
|
return `${pluginKey}:${version}:${lastUpdated}`;
|
|
@@ -14431,7 +14469,7 @@ async function computeConfigHash(sageVersion, ...dirs) {
|
|
|
14431
14469
|
}
|
|
14432
14470
|
for (const file of files) {
|
|
14433
14471
|
try {
|
|
14434
|
-
const content = await getFileContent((0,
|
|
14472
|
+
const content = await getFileContent((0, import_node_path9.join)(dir, file));
|
|
14435
14473
|
h.update(content);
|
|
14436
14474
|
} catch {
|
|
14437
14475
|
}
|
|
@@ -14524,12 +14562,14 @@ function storeResult(cache, pluginKey, version, lastUpdated, findings) {
|
|
|
14524
14562
|
var import_node_crypto6 = require("node:crypto");
|
|
14525
14563
|
var import_promises6 = require("node:fs/promises");
|
|
14526
14564
|
var import_node_os4 = require("node:os");
|
|
14527
|
-
var
|
|
14528
|
-
var DEFAULT_PLUGINS_REGISTRY = (0,
|
|
14565
|
+
var import_node_path10 = require("node:path");
|
|
14566
|
+
var DEFAULT_PLUGINS_REGISTRY = (0, import_node_path10.join)((0, import_node_os4.homedir)(), ".claude", "plugins", "installed_plugins.json");
|
|
14529
14567
|
var SCANNABLE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
14530
14568
|
".py",
|
|
14531
14569
|
".js",
|
|
14532
14570
|
".ts",
|
|
14571
|
+
".mjs",
|
|
14572
|
+
".mts",
|
|
14533
14573
|
".sh",
|
|
14534
14574
|
".bash",
|
|
14535
14575
|
".zsh",
|
|
@@ -14555,7 +14595,7 @@ async function walkPluginFiles(installPath, logger) {
|
|
|
14555
14595
|
return;
|
|
14556
14596
|
}
|
|
14557
14597
|
if (stats.isFile()) {
|
|
14558
|
-
if (SCANNABLE_EXTENSIONS.has((0,
|
|
14598
|
+
if (SCANNABLE_EXTENSIONS.has((0, import_node_path10.extname)(dirOrFile).toLowerCase()) && stats.size <= MAX_FILE_SIZE) {
|
|
14559
14599
|
files.push(dirOrFile);
|
|
14560
14600
|
}
|
|
14561
14601
|
return;
|
|
@@ -14570,7 +14610,7 @@ async function walkPluginFiles(installPath, logger) {
|
|
|
14570
14610
|
for (const entry of entries) {
|
|
14571
14611
|
if (SKIP_DIRS.has(entry))
|
|
14572
14612
|
continue;
|
|
14573
|
-
const fullPath = (0,
|
|
14613
|
+
const fullPath = (0, import_node_path10.join)(dirOrFile, entry);
|
|
14574
14614
|
await walk(fullPath);
|
|
14575
14615
|
}
|
|
14576
14616
|
}
|
|
@@ -14588,6 +14628,82 @@ function isHarmlessEcho(line) {
|
|
|
14588
14628
|
const withoutQuotes = line.replace(/"(?:[^"\\]|\\.)*"|'[^']*'/g, "");
|
|
14589
14629
|
return !withoutQuotes.includes("|");
|
|
14590
14630
|
}
|
|
14631
|
+
var JS_TS_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".ts", ".mjs", ".mts"]);
|
|
14632
|
+
var STRING_OR_COMMENT_RE = /"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|`(?:[^`\\]|\\.)*`|\/\/.*$|\/\*[\s\S]*?\*\//gm;
|
|
14633
|
+
function stripJsComments(src) {
|
|
14634
|
+
return src.replace(STRING_OR_COMMENT_RE, (match) => {
|
|
14635
|
+
if (match.startsWith("//") || match.startsWith("/*"))
|
|
14636
|
+
return "";
|
|
14637
|
+
return match;
|
|
14638
|
+
});
|
|
14639
|
+
}
|
|
14640
|
+
var STR_ARG = `(?:"((?:[^"\\\\]|\\\\.)*)"|'((?:[^'\\\\]|\\\\.)*)'|\`([^\`]*)\`)`;
|
|
14641
|
+
var JS_EXEC_RE = new RegExp(`\\bexec(?:File)?(?:Sync)?\\s*\\(\\s*${STR_ARG}`, "g");
|
|
14642
|
+
var JS_SPAWN_RE = new RegExp(`\\bspawn(?:Sync)?\\s*\\(\\s*${STR_ARG}`, "g");
|
|
14643
|
+
var JS_SPAWN_ARGS_RE = /\bspawn(?:Sync)?\s*\(\s*(?:"((?:[^"\\]|\\.)*)"|'((?:[^'\\]|\\.)*)'|`([^`]*)`)\s*,\s*\[([^\]]*)\]/g;
|
|
14644
|
+
var SHELL_EXECUTABLES = /* @__PURE__ */ new Set([
|
|
14645
|
+
"bash",
|
|
14646
|
+
"sh",
|
|
14647
|
+
"zsh",
|
|
14648
|
+
"dash",
|
|
14649
|
+
"ksh",
|
|
14650
|
+
"csh",
|
|
14651
|
+
"fish",
|
|
14652
|
+
"cmd",
|
|
14653
|
+
"cmd.exe",
|
|
14654
|
+
"powershell",
|
|
14655
|
+
"powershell.exe",
|
|
14656
|
+
"pwsh"
|
|
14657
|
+
]);
|
|
14658
|
+
function isShellExecutable(exe) {
|
|
14659
|
+
const basename = exe.split("/").pop()?.split("\\").pop() ?? exe;
|
|
14660
|
+
return SHELL_EXECUTABLES.has(basename);
|
|
14661
|
+
}
|
|
14662
|
+
var JS_EXECA_RE = new RegExp(`\\bexeca\\s*\\(\\s*${STR_ARG}`, "g");
|
|
14663
|
+
var JS_EXECA_ARGS_RE = /\bexeca\s*\(\s*(?:"((?:[^"\\]|\\.)*)"|'((?:[^'\\]|\\.)*)'|`([^`]*)`)\s*,\s*\[([^\]]*)\]/g;
|
|
14664
|
+
var JS_BUN_SHELL_RE = new RegExp(`\\bBun\\.shell\\s*\\(\\s*${STR_ARG}`, "g");
|
|
14665
|
+
var JS_BUN_DOLLAR_RE = /\bBun\.\$`([^`]+)`/g;
|
|
14666
|
+
var JS_ZX_RE = /(?<!\w)\$`([^`]+)`/g;
|
|
14667
|
+
function extractCommandsFromJsTs(content, fileName) {
|
|
14668
|
+
const commands = /* @__PURE__ */ new Set();
|
|
14669
|
+
const stripped = stripJsComments(content);
|
|
14670
|
+
const collapsed = stripped.replace(/\n/g, " ");
|
|
14671
|
+
function addMatch(m) {
|
|
14672
|
+
const val = m[1] ?? m[2] ?? m[3];
|
|
14673
|
+
if (val)
|
|
14674
|
+
commands.add(val.trim());
|
|
14675
|
+
}
|
|
14676
|
+
for (const re of [JS_EXEC_RE, JS_SPAWN_RE, JS_EXECA_RE, JS_BUN_SHELL_RE]) {
|
|
14677
|
+
for (const m of collapsed.matchAll(re)) {
|
|
14678
|
+
addMatch(m);
|
|
14679
|
+
}
|
|
14680
|
+
}
|
|
14681
|
+
for (const re of [JS_BUN_DOLLAR_RE, JS_ZX_RE]) {
|
|
14682
|
+
for (const m of collapsed.matchAll(re)) {
|
|
14683
|
+
if (m[1])
|
|
14684
|
+
commands.add(m[1].trim());
|
|
14685
|
+
}
|
|
14686
|
+
}
|
|
14687
|
+
for (const re of [JS_SPAWN_ARGS_RE, JS_EXECA_ARGS_RE]) {
|
|
14688
|
+
for (const arrMatch of collapsed.matchAll(re)) {
|
|
14689
|
+
const exe = (arrMatch[1] ?? arrMatch[2] ?? arrMatch[3] ?? "").trim();
|
|
14690
|
+
if (!isShellExecutable(exe))
|
|
14691
|
+
continue;
|
|
14692
|
+
const arrayContent = arrMatch[4] ?? "";
|
|
14693
|
+
const strLitRe = new RegExp(STR_ARG, "g");
|
|
14694
|
+
for (const strMatch of arrayContent.matchAll(strLitRe)) {
|
|
14695
|
+
const val = strMatch[1] ?? strMatch[2] ?? strMatch[3];
|
|
14696
|
+
if (val)
|
|
14697
|
+
commands.add(val.trim());
|
|
14698
|
+
}
|
|
14699
|
+
}
|
|
14700
|
+
}
|
|
14701
|
+
return [...commands].map((value) => ({
|
|
14702
|
+
type: "command",
|
|
14703
|
+
value,
|
|
14704
|
+
context: `plugin_file:${fileName}`
|
|
14705
|
+
}));
|
|
14706
|
+
}
|
|
14591
14707
|
function extractArtifactsFromFile(filePath, content) {
|
|
14592
14708
|
const artifacts = [];
|
|
14593
14709
|
const fileName = filePath.split("/").pop() ?? filePath;
|
|
@@ -14596,7 +14712,7 @@ function extractArtifactsFromFile(filePath, content) {
|
|
|
14596
14712
|
continue;
|
|
14597
14713
|
artifacts.push({ type: "url", value: url, context: `plugin_file:${fileName}` });
|
|
14598
14714
|
}
|
|
14599
|
-
const ext = (0,
|
|
14715
|
+
const ext = (0, import_node_path10.extname)(filePath).toLowerCase();
|
|
14600
14716
|
if ([".sh", ".bash", ".zsh", ".py"].includes(ext)) {
|
|
14601
14717
|
for (const line of content.split("\n")) {
|
|
14602
14718
|
const trimmed = line.trim();
|
|
@@ -14609,6 +14725,9 @@ function extractArtifactsFromFile(filePath, content) {
|
|
|
14609
14725
|
}
|
|
14610
14726
|
}
|
|
14611
14727
|
}
|
|
14728
|
+
if (JS_TS_EXTENSIONS.has(ext)) {
|
|
14729
|
+
artifacts.push(...extractCommandsFromJsTs(content, fileName));
|
|
14730
|
+
}
|
|
14612
14731
|
return artifacts;
|
|
14613
14732
|
}
|
|
14614
14733
|
async function scanPlugin(plugin, threats, options = {}) {
|
|
@@ -14641,7 +14760,7 @@ async function scanPlugin(plugin, threats, options = {}) {
|
|
|
14641
14760
|
confidence: match.threat.confidence,
|
|
14642
14761
|
action: match.threat.action,
|
|
14643
14762
|
artifact: match.artifact.slice(0, 200),
|
|
14644
|
-
sourceFile: (0,
|
|
14763
|
+
sourceFile: (0, import_node_path10.relative)(plugin.installPath, filePath)
|
|
14645
14764
|
});
|
|
14646
14765
|
}
|
|
14647
14766
|
}
|
|
@@ -14696,7 +14815,7 @@ async function scanPlugin(plugin, threats, options = {}) {
|
|
|
14696
14815
|
confidence: 1,
|
|
14697
14816
|
action: "block",
|
|
14698
14817
|
artifact: fr.sha256,
|
|
14699
|
-
sourceFile: (0,
|
|
14818
|
+
sourceFile: (0, import_node_path10.relative)(plugin.installPath, filePath)
|
|
14700
14819
|
});
|
|
14701
14820
|
}
|
|
14702
14821
|
}
|
|
@@ -14969,20 +15088,19 @@ function createAllowlistRemoveTool(logger) {
|
|
|
14969
15088
|
}
|
|
14970
15089
|
|
|
14971
15090
|
// src/bundled-dirs.ts
|
|
14972
|
-
var
|
|
14973
|
-
var
|
|
14974
|
-
var packageRoot = (0, import_node_path10.resolve)(__dirname, "..");
|
|
15091
|
+
var import_node_path11 = require("node:path");
|
|
15092
|
+
var packageRoot = (0, import_node_path11.resolve)(__dirname, "..");
|
|
14975
15093
|
function getBundledDataDirs() {
|
|
14976
15094
|
return {
|
|
14977
|
-
threatsDir: (0,
|
|
14978
|
-
allowlistsDir: (0,
|
|
15095
|
+
threatsDir: (0, import_node_path11.join)(packageRoot, "resources", "threats"),
|
|
15096
|
+
allowlistsDir: (0, import_node_path11.join)(packageRoot, "resources", "allowlists")
|
|
14979
15097
|
};
|
|
14980
15098
|
}
|
|
14981
15099
|
var cachedVersion;
|
|
14982
15100
|
function getSageVersion() {
|
|
14983
15101
|
if (cachedVersion) return cachedVersion;
|
|
14984
15102
|
try {
|
|
14985
|
-
const pkg = JSON.parse((
|
|
15103
|
+
const pkg = JSON.parse(getFileContentSync((0, import_node_path11.join)(packageRoot, "package.json"), "utf-8"));
|
|
14986
15104
|
cachedVersion = pkg.version ?? "0.0.0";
|
|
14987
15105
|
} catch {
|
|
14988
15106
|
cachedVersion = "0.0.0";
|
|
@@ -15048,8 +15166,8 @@ function createLogger(pluginLogger) {
|
|
|
15048
15166
|
// src/plugin-discovery.ts
|
|
15049
15167
|
var import_promises7 = require("node:fs/promises");
|
|
15050
15168
|
var import_node_os6 = require("node:os");
|
|
15051
|
-
var
|
|
15052
|
-
var DEFAULT_EXTENSIONS_DIR = (0,
|
|
15169
|
+
var import_node_path12 = require("node:path");
|
|
15170
|
+
var DEFAULT_EXTENSIONS_DIR = (0, import_node_path12.join)((0, import_node_os6.homedir)(), ".openclaw", "extensions");
|
|
15053
15171
|
async function discoverOpenClawPlugins(logger, extensionsDir = DEFAULT_EXTENSIONS_DIR) {
|
|
15054
15172
|
logger.info("Sage plugin discovery: scanning extensions directory", { path: extensionsDir });
|
|
15055
15173
|
let entries;
|
|
@@ -15061,7 +15179,7 @@ async function discoverOpenClawPlugins(logger, extensionsDir = DEFAULT_EXTENSION
|
|
|
15061
15179
|
}
|
|
15062
15180
|
const plugins = [];
|
|
15063
15181
|
for (const entry of entries) {
|
|
15064
|
-
const extDir = (0,
|
|
15182
|
+
const extDir = (0, import_node_path12.join)(extensionsDir, entry);
|
|
15065
15183
|
let stats;
|
|
15066
15184
|
try {
|
|
15067
15185
|
stats = await (0, import_promises7.stat)(extDir);
|
|
@@ -15071,7 +15189,7 @@ async function discoverOpenClawPlugins(logger, extensionsDir = DEFAULT_EXTENSION
|
|
|
15071
15189
|
}
|
|
15072
15190
|
let pkg;
|
|
15073
15191
|
try {
|
|
15074
|
-
const pkgPath = (0,
|
|
15192
|
+
const pkgPath = (0, import_node_path12.join)(extDir, "package.json");
|
|
15075
15193
|
const raw = await getFileContent(pkgPath);
|
|
15076
15194
|
pkg = JSON.parse(raw);
|
|
15077
15195
|
} catch {
|
|
@@ -15101,7 +15219,7 @@ function createOpenClawScanHandler(logger, context, onResult) {
|
|
|
15101
15219
|
logger,
|
|
15102
15220
|
context,
|
|
15103
15221
|
discoverPlugins: () => discoverOpenClawPlugins(logger),
|
|
15104
|
-
selfPrefix: "sage-openclaw@",
|
|
15222
|
+
selfPrefix: "@gendigital/sage-openclaw@",
|
|
15105
15223
|
threatsDir,
|
|
15106
15224
|
allowlistsDir,
|
|
15107
15225
|
version,
|