@gendigital/sage-openclaw 0.5.4 → 0.7.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 +921 -404
- package/dist/index.cjs.map +4 -4
- package/openclaw.plugin.json +1 -1
- package/package.json +14 -11
- package/resources/skills/security-awareness/SKILL.md +9 -0
- package/resources/threats/commands.yaml +2 -2
- package/resources/threats/mitre.yaml +2 -2
- package/resources/threats/self-defense.yaml +2 -2
package/dist/index.cjs
CHANGED
|
@@ -3978,10 +3978,10 @@ var require_resolve_block_map = __commonJS({
|
|
|
3978
3978
|
let offset = bm.offset;
|
|
3979
3979
|
let commentEnd = null;
|
|
3980
3980
|
for (const collItem of bm.items) {
|
|
3981
|
-
const { start, key, sep:
|
|
3981
|
+
const { start, key, sep: sep3, value } = collItem;
|
|
3982
3982
|
const keyProps = resolveProps.resolveProps(start, {
|
|
3983
3983
|
indicator: "explicit-key-ind",
|
|
3984
|
-
next: key ??
|
|
3984
|
+
next: key ?? sep3?.[0],
|
|
3985
3985
|
offset,
|
|
3986
3986
|
onError,
|
|
3987
3987
|
parentIndent: bm.indent,
|
|
@@ -3995,7 +3995,7 @@ var require_resolve_block_map = __commonJS({
|
|
|
3995
3995
|
else if ("indent" in key && key.indent !== bm.indent)
|
|
3996
3996
|
onError(offset, "BAD_INDENT", startColMsg);
|
|
3997
3997
|
}
|
|
3998
|
-
if (!keyProps.anchor && !keyProps.tag && !
|
|
3998
|
+
if (!keyProps.anchor && !keyProps.tag && !sep3) {
|
|
3999
3999
|
commentEnd = keyProps.end;
|
|
4000
4000
|
if (keyProps.comment) {
|
|
4001
4001
|
if (map.comment)
|
|
@@ -4019,7 +4019,7 @@ var require_resolve_block_map = __commonJS({
|
|
|
4019
4019
|
ctx.atKey = false;
|
|
4020
4020
|
if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode))
|
|
4021
4021
|
onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique");
|
|
4022
|
-
const valueProps = resolveProps.resolveProps(
|
|
4022
|
+
const valueProps = resolveProps.resolveProps(sep3 ?? [], {
|
|
4023
4023
|
indicator: "map-value-ind",
|
|
4024
4024
|
next: value,
|
|
4025
4025
|
offset: keyNode.range[2],
|
|
@@ -4035,7 +4035,7 @@ var require_resolve_block_map = __commonJS({
|
|
|
4035
4035
|
if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024)
|
|
4036
4036
|
onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key");
|
|
4037
4037
|
}
|
|
4038
|
-
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset,
|
|
4038
|
+
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep3, null, valueProps, onError);
|
|
4039
4039
|
if (ctx.schema.compat)
|
|
4040
4040
|
utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError);
|
|
4041
4041
|
offset = valueNode.range[2];
|
|
@@ -4126,7 +4126,7 @@ var require_resolve_end = __commonJS({
|
|
|
4126
4126
|
let comment = "";
|
|
4127
4127
|
if (end) {
|
|
4128
4128
|
let hasSpace = false;
|
|
4129
|
-
let
|
|
4129
|
+
let sep3 = "";
|
|
4130
4130
|
for (const token of end) {
|
|
4131
4131
|
const { source, type } = token;
|
|
4132
4132
|
switch (type) {
|
|
@@ -4140,13 +4140,13 @@ var require_resolve_end = __commonJS({
|
|
|
4140
4140
|
if (!comment)
|
|
4141
4141
|
comment = cb;
|
|
4142
4142
|
else
|
|
4143
|
-
comment +=
|
|
4144
|
-
|
|
4143
|
+
comment += sep3 + cb;
|
|
4144
|
+
sep3 = "";
|
|
4145
4145
|
break;
|
|
4146
4146
|
}
|
|
4147
4147
|
case "newline":
|
|
4148
4148
|
if (comment)
|
|
4149
|
-
|
|
4149
|
+
sep3 += source;
|
|
4150
4150
|
hasSpace = true;
|
|
4151
4151
|
break;
|
|
4152
4152
|
default:
|
|
@@ -4189,18 +4189,18 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
4189
4189
|
let offset = fc.offset + fc.start.source.length;
|
|
4190
4190
|
for (let i = 0; i < fc.items.length; ++i) {
|
|
4191
4191
|
const collItem = fc.items[i];
|
|
4192
|
-
const { start, key, sep:
|
|
4192
|
+
const { start, key, sep: sep3, value } = collItem;
|
|
4193
4193
|
const props = resolveProps.resolveProps(start, {
|
|
4194
4194
|
flow: fcName,
|
|
4195
4195
|
indicator: "explicit-key-ind",
|
|
4196
|
-
next: key ??
|
|
4196
|
+
next: key ?? sep3?.[0],
|
|
4197
4197
|
offset,
|
|
4198
4198
|
onError,
|
|
4199
4199
|
parentIndent: fc.indent,
|
|
4200
4200
|
startOnNewline: false
|
|
4201
4201
|
});
|
|
4202
4202
|
if (!props.found) {
|
|
4203
|
-
if (!props.anchor && !props.tag && !
|
|
4203
|
+
if (!props.anchor && !props.tag && !sep3 && !value) {
|
|
4204
4204
|
if (i === 0 && props.comma)
|
|
4205
4205
|
onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`);
|
|
4206
4206
|
else if (i < fc.items.length - 1)
|
|
@@ -4254,8 +4254,8 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
4254
4254
|
}
|
|
4255
4255
|
}
|
|
4256
4256
|
}
|
|
4257
|
-
if (!isMap && !
|
|
4258
|
-
const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end,
|
|
4257
|
+
if (!isMap && !sep3 && !props.found) {
|
|
4258
|
+
const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep3, null, props, onError);
|
|
4259
4259
|
coll.items.push(valueNode);
|
|
4260
4260
|
offset = valueNode.range[2];
|
|
4261
4261
|
if (isBlock(value))
|
|
@@ -4267,7 +4267,7 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
4267
4267
|
if (isBlock(key))
|
|
4268
4268
|
onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg);
|
|
4269
4269
|
ctx.atKey = false;
|
|
4270
|
-
const valueProps = resolveProps.resolveProps(
|
|
4270
|
+
const valueProps = resolveProps.resolveProps(sep3 ?? [], {
|
|
4271
4271
|
flow: fcName,
|
|
4272
4272
|
indicator: "map-value-ind",
|
|
4273
4273
|
next: value,
|
|
@@ -4278,8 +4278,8 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
4278
4278
|
});
|
|
4279
4279
|
if (valueProps.found) {
|
|
4280
4280
|
if (!isMap && !props.found && ctx.options.strict) {
|
|
4281
|
-
if (
|
|
4282
|
-
for (const st of
|
|
4281
|
+
if (sep3)
|
|
4282
|
+
for (const st of sep3) {
|
|
4283
4283
|
if (st === valueProps.found)
|
|
4284
4284
|
break;
|
|
4285
4285
|
if (st.type === "newline") {
|
|
@@ -4296,7 +4296,7 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
4296
4296
|
else
|
|
4297
4297
|
onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`);
|
|
4298
4298
|
}
|
|
4299
|
-
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end,
|
|
4299
|
+
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep3, null, valueProps, onError) : null;
|
|
4300
4300
|
if (valueNode) {
|
|
4301
4301
|
if (isBlock(value))
|
|
4302
4302
|
onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg);
|
|
@@ -4476,7 +4476,7 @@ var require_resolve_block_scalar = __commonJS({
|
|
|
4476
4476
|
chompStart = i + 1;
|
|
4477
4477
|
}
|
|
4478
4478
|
let value = "";
|
|
4479
|
-
let
|
|
4479
|
+
let sep3 = "";
|
|
4480
4480
|
let prevMoreIndented = false;
|
|
4481
4481
|
for (let i = 0; i < contentStart; ++i)
|
|
4482
4482
|
value += lines[i][0].slice(trimIndent) + "\n";
|
|
@@ -4493,24 +4493,24 @@ var require_resolve_block_scalar = __commonJS({
|
|
|
4493
4493
|
indent = "";
|
|
4494
4494
|
}
|
|
4495
4495
|
if (type === Scalar.Scalar.BLOCK_LITERAL) {
|
|
4496
|
-
value +=
|
|
4497
|
-
|
|
4496
|
+
value += sep3 + indent.slice(trimIndent) + content;
|
|
4497
|
+
sep3 = "\n";
|
|
4498
4498
|
} else if (indent.length > trimIndent || content[0] === " ") {
|
|
4499
|
-
if (
|
|
4500
|
-
|
|
4501
|
-
else if (!prevMoreIndented &&
|
|
4502
|
-
|
|
4503
|
-
value +=
|
|
4504
|
-
|
|
4499
|
+
if (sep3 === " ")
|
|
4500
|
+
sep3 = "\n";
|
|
4501
|
+
else if (!prevMoreIndented && sep3 === "\n")
|
|
4502
|
+
sep3 = "\n\n";
|
|
4503
|
+
value += sep3 + indent.slice(trimIndent) + content;
|
|
4504
|
+
sep3 = "\n";
|
|
4505
4505
|
prevMoreIndented = true;
|
|
4506
4506
|
} else if (content === "") {
|
|
4507
|
-
if (
|
|
4507
|
+
if (sep3 === "\n")
|
|
4508
4508
|
value += "\n";
|
|
4509
4509
|
else
|
|
4510
|
-
|
|
4510
|
+
sep3 = "\n";
|
|
4511
4511
|
} else {
|
|
4512
|
-
value +=
|
|
4513
|
-
|
|
4512
|
+
value += sep3 + content;
|
|
4513
|
+
sep3 = " ";
|
|
4514
4514
|
prevMoreIndented = false;
|
|
4515
4515
|
}
|
|
4516
4516
|
}
|
|
@@ -4692,25 +4692,25 @@ var require_resolve_flow_scalar = __commonJS({
|
|
|
4692
4692
|
if (!match)
|
|
4693
4693
|
return source;
|
|
4694
4694
|
let res = match[1];
|
|
4695
|
-
let
|
|
4695
|
+
let sep3 = " ";
|
|
4696
4696
|
let pos = first.lastIndex;
|
|
4697
4697
|
line.lastIndex = pos;
|
|
4698
4698
|
while (match = line.exec(source)) {
|
|
4699
4699
|
if (match[1] === "") {
|
|
4700
|
-
if (
|
|
4701
|
-
res +=
|
|
4700
|
+
if (sep3 === "\n")
|
|
4701
|
+
res += sep3;
|
|
4702
4702
|
else
|
|
4703
|
-
|
|
4703
|
+
sep3 = "\n";
|
|
4704
4704
|
} else {
|
|
4705
|
-
res +=
|
|
4706
|
-
|
|
4705
|
+
res += sep3 + match[1];
|
|
4706
|
+
sep3 = " ";
|
|
4707
4707
|
}
|
|
4708
4708
|
pos = line.lastIndex;
|
|
4709
4709
|
}
|
|
4710
4710
|
const last = /[ \t]*(.*)/sy;
|
|
4711
4711
|
last.lastIndex = pos;
|
|
4712
4712
|
match = last.exec(source);
|
|
4713
|
-
return res +
|
|
4713
|
+
return res + sep3 + (match?.[1] ?? "");
|
|
4714
4714
|
}
|
|
4715
4715
|
function doubleQuotedValue(source, onError) {
|
|
4716
4716
|
let res = "";
|
|
@@ -5511,14 +5511,14 @@ var require_cst_stringify = __commonJS({
|
|
|
5511
5511
|
}
|
|
5512
5512
|
}
|
|
5513
5513
|
}
|
|
5514
|
-
function stringifyItem({ start, key, sep:
|
|
5514
|
+
function stringifyItem({ start, key, sep: sep3, value }) {
|
|
5515
5515
|
let res = "";
|
|
5516
5516
|
for (const st of start)
|
|
5517
5517
|
res += st.source;
|
|
5518
5518
|
if (key)
|
|
5519
5519
|
res += stringifyToken(key);
|
|
5520
|
-
if (
|
|
5521
|
-
for (const st of
|
|
5520
|
+
if (sep3)
|
|
5521
|
+
for (const st of sep3)
|
|
5522
5522
|
res += st.source;
|
|
5523
5523
|
if (value)
|
|
5524
5524
|
res += stringifyToken(value);
|
|
@@ -6667,18 +6667,18 @@ var require_parser = __commonJS({
|
|
|
6667
6667
|
if (this.type === "map-value-ind") {
|
|
6668
6668
|
const prev = getPrevProps(this.peek(2));
|
|
6669
6669
|
const start = getFirstKeyStartProps(prev);
|
|
6670
|
-
let
|
|
6670
|
+
let sep3;
|
|
6671
6671
|
if (scalar.end) {
|
|
6672
|
-
|
|
6673
|
-
|
|
6672
|
+
sep3 = scalar.end;
|
|
6673
|
+
sep3.push(this.sourceToken);
|
|
6674
6674
|
delete scalar.end;
|
|
6675
6675
|
} else
|
|
6676
|
-
|
|
6676
|
+
sep3 = [this.sourceToken];
|
|
6677
6677
|
const map = {
|
|
6678
6678
|
type: "block-map",
|
|
6679
6679
|
offset: scalar.offset,
|
|
6680
6680
|
indent: scalar.indent,
|
|
6681
|
-
items: [{ start, key: scalar, sep:
|
|
6681
|
+
items: [{ start, key: scalar, sep: sep3 }]
|
|
6682
6682
|
};
|
|
6683
6683
|
this.onKeyLine = true;
|
|
6684
6684
|
this.stack[this.stack.length - 1] = map;
|
|
@@ -6831,15 +6831,15 @@ var require_parser = __commonJS({
|
|
|
6831
6831
|
} else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) {
|
|
6832
6832
|
const start2 = getFirstKeyStartProps(it.start);
|
|
6833
6833
|
const key = it.key;
|
|
6834
|
-
const
|
|
6835
|
-
|
|
6834
|
+
const sep3 = it.sep;
|
|
6835
|
+
sep3.push(this.sourceToken);
|
|
6836
6836
|
delete it.key;
|
|
6837
6837
|
delete it.sep;
|
|
6838
6838
|
this.stack.push({
|
|
6839
6839
|
type: "block-map",
|
|
6840
6840
|
offset: this.offset,
|
|
6841
6841
|
indent: this.indent,
|
|
6842
|
-
items: [{ start: start2, key, sep:
|
|
6842
|
+
items: [{ start: start2, key, sep: sep3 }]
|
|
6843
6843
|
});
|
|
6844
6844
|
} else if (start.length > 0) {
|
|
6845
6845
|
it.sep = it.sep.concat(start, this.sourceToken);
|
|
@@ -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":
|
|
@@ -7033,13 +7033,13 @@ var require_parser = __commonJS({
|
|
|
7033
7033
|
const prev = getPrevProps(parent);
|
|
7034
7034
|
const start = getFirstKeyStartProps(prev);
|
|
7035
7035
|
fixFlowSeqItems(fc);
|
|
7036
|
-
const
|
|
7037
|
-
|
|
7036
|
+
const sep3 = fc.end.splice(1, fc.end.length);
|
|
7037
|
+
sep3.push(this.sourceToken);
|
|
7038
7038
|
const map = {
|
|
7039
7039
|
type: "block-map",
|
|
7040
7040
|
offset: fc.offset,
|
|
7041
7041
|
indent: fc.indent,
|
|
7042
|
-
items: [{ start, key: fc, sep:
|
|
7042
|
+
items: [{ start, key: fc, sep: sep3 }]
|
|
7043
7043
|
};
|
|
7044
7044
|
this.onKeyLine = true;
|
|
7045
7045
|
this.stack[this.stack.length - 1] = map;
|
|
@@ -7325,21 +7325,31 @@ __export(index_exports, {
|
|
|
7325
7325
|
module.exports = __toCommonJS(index_exports);
|
|
7326
7326
|
|
|
7327
7327
|
// ../core/dist/config.js
|
|
7328
|
-
var import_node_os = require("node:os");
|
|
7329
7328
|
var import_node_path2 = require("node:path");
|
|
7330
7329
|
|
|
7331
7330
|
// ../core/dist/file-utils.js
|
|
7332
7331
|
var import_node_crypto = require("node:crypto");
|
|
7332
|
+
var fs = __toESM(require("node:fs"), 1);
|
|
7333
7333
|
var fsPromises = __toESM(require("node:fs/promises"), 1);
|
|
7334
|
+
var import_node_os = require("node:os");
|
|
7334
7335
|
var import_node_path = require("node:path");
|
|
7335
7336
|
var name1 = "read";
|
|
7336
7337
|
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
|
}
|
|
7347
|
+
function getProcEnv() {
|
|
7348
|
+
return process["env"];
|
|
7349
|
+
}
|
|
7350
|
+
function getHomeDir() {
|
|
7351
|
+
return getProcEnv().HOME || (0, import_node_os.homedir)();
|
|
7352
|
+
}
|
|
7343
7353
|
async function atomicWriteJson(path, data) {
|
|
7344
7354
|
await fsPromises.mkdir((0, import_node_path.dirname)(path), { recursive: true });
|
|
7345
7355
|
const tmp = `${path}.${(0, import_node_crypto.randomBytes)(6).toString("hex")}.tmp`;
|
|
@@ -11437,6 +11447,7 @@ var SeveritySchema = external_exports.enum(["critical", "high", "medium", "low"]
|
|
|
11437
11447
|
var ActionSchema = external_exports.enum(["block", "require_approval", "log"]);
|
|
11438
11448
|
var ThreatSchema = external_exports.object({
|
|
11439
11449
|
id: external_exports.string(),
|
|
11450
|
+
version: external_exports.number().int().optional(),
|
|
11440
11451
|
category: external_exports.string(),
|
|
11441
11452
|
severity: SeveritySchema,
|
|
11442
11453
|
confidence: external_exports.number(),
|
|
@@ -11485,6 +11496,21 @@ var PackageCheckConfigSchema = external_exports.object({
|
|
|
11485
11496
|
var AmsiCheckConfigSchema = external_exports.object({
|
|
11486
11497
|
enabled: external_exports.boolean().default(true)
|
|
11487
11498
|
});
|
|
11499
|
+
var ExceptionDecisionSchema = external_exports.enum(["allow", "deny"]);
|
|
11500
|
+
var ExceptionMatchSchema = external_exports.enum(["executable", "domain", "path", "plugin", "regex"]);
|
|
11501
|
+
var ExceptionRuleSchema = external_exports.object({
|
|
11502
|
+
id: external_exports.string().optional(),
|
|
11503
|
+
decision: ExceptionDecisionSchema,
|
|
11504
|
+
match: ExceptionMatchSchema,
|
|
11505
|
+
pattern: external_exports.string(),
|
|
11506
|
+
reason: external_exports.string().optional()
|
|
11507
|
+
});
|
|
11508
|
+
var ExceptionsFileSchema = external_exports.object({
|
|
11509
|
+
rules: external_exports.array(ExceptionRuleSchema).default([])
|
|
11510
|
+
});
|
|
11511
|
+
var ExceptionsConfigSchema = external_exports.object({
|
|
11512
|
+
path: external_exports.string().default("~/.sage/exceptions.json")
|
|
11513
|
+
});
|
|
11488
11514
|
var ConfigSchema = external_exports.object({
|
|
11489
11515
|
url_check: UrlCheckConfigSchema.default({}),
|
|
11490
11516
|
file_check: FileCheckConfigSchema.default({}),
|
|
@@ -11493,17 +11519,22 @@ var ConfigSchema = external_exports.object({
|
|
|
11493
11519
|
heuristics_enabled: external_exports.boolean().default(true),
|
|
11494
11520
|
cache: CacheConfigSchema.default({}),
|
|
11495
11521
|
allowlist: AllowlistConfigSchema.default({}),
|
|
11522
|
+
exceptions: ExceptionsConfigSchema.default({}),
|
|
11496
11523
|
logging: LoggingConfigSchema.default({}),
|
|
11497
11524
|
sensitivity: SensitivitySchema.default("balanced"),
|
|
11498
11525
|
disabled_threats: external_exports.array(external_exports.string()).default([])
|
|
11499
11526
|
});
|
|
11527
|
+
var HookTypeSchema = external_exports.enum([
|
|
11528
|
+
"PreToolUse",
|
|
11529
|
+
"PostToolUse",
|
|
11530
|
+
"SessionStart",
|
|
11531
|
+
"GatewayStart",
|
|
11532
|
+
"BeforeAgentStart",
|
|
11533
|
+
"MessagesTransform"
|
|
11534
|
+
]);
|
|
11500
11535
|
|
|
11501
11536
|
// ../core/dist/config.js
|
|
11502
11537
|
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
11538
|
function resolvedSageDir() {
|
|
11508
11539
|
return resolvePath(SAGE_DIR);
|
|
11509
11540
|
}
|
|
@@ -11516,12 +11547,16 @@ function defaultCachePath() {
|
|
|
11516
11547
|
function defaultAllowlistPath() {
|
|
11517
11548
|
return (0, import_node_path2.join)(resolvedSageDir(), "allowlist.json");
|
|
11518
11549
|
}
|
|
11550
|
+
function defaultExceptionsPath() {
|
|
11551
|
+
return (0, import_node_path2.join)(resolvedSageDir(), "exceptions.json");
|
|
11552
|
+
}
|
|
11519
11553
|
function defaultAuditPath() {
|
|
11520
11554
|
return (0, import_node_path2.join)(resolvedSageDir(), "audit.jsonl");
|
|
11521
11555
|
}
|
|
11522
11556
|
function resolvePath(pathStr) {
|
|
11523
11557
|
if (pathStr.startsWith("~/") || pathStr === "~") {
|
|
11524
|
-
|
|
11558
|
+
const home = getHomeDir();
|
|
11559
|
+
return (0, import_node_path2.join)(home, pathStr.slice(1));
|
|
11525
11560
|
}
|
|
11526
11561
|
return pathStr;
|
|
11527
11562
|
}
|
|
@@ -11564,6 +11599,7 @@ function normalizeStateFilePath(configuredPath, fallbackPath, field, logger) {
|
|
|
11564
11599
|
function sanitizeConfigPaths(config, logger) {
|
|
11565
11600
|
const cachePath = defaultCachePath();
|
|
11566
11601
|
const allowlistPath = defaultAllowlistPath();
|
|
11602
|
+
const exceptionsPath = defaultExceptionsPath();
|
|
11567
11603
|
const auditPath = defaultAuditPath();
|
|
11568
11604
|
return {
|
|
11569
11605
|
...config,
|
|
@@ -11575,6 +11611,10 @@ function sanitizeConfigPaths(config, logger) {
|
|
|
11575
11611
|
...config.allowlist,
|
|
11576
11612
|
path: normalizeStateFilePath(config.allowlist.path, allowlistPath, "allowlist", logger)
|
|
11577
11613
|
},
|
|
11614
|
+
exceptions: {
|
|
11615
|
+
...config.exceptions,
|
|
11616
|
+
path: normalizeStateFilePath(config.exceptions.path, exceptionsPath, "exceptions", logger)
|
|
11617
|
+
},
|
|
11578
11618
|
logging: {
|
|
11579
11619
|
...config.logging,
|
|
11580
11620
|
path: normalizeStateFilePath(config.logging.path, auditPath, "logging", logger)
|
|
@@ -11610,7 +11650,6 @@ async function loadConfig(configPath, logger = nullLogger) {
|
|
|
11610
11650
|
|
|
11611
11651
|
// ../core/dist/url-utils.js
|
|
11612
11652
|
var import_node_crypto2 = require("node:crypto");
|
|
11613
|
-
var import_node_os2 = require("node:os");
|
|
11614
11653
|
var import_node_path3 = require("node:path");
|
|
11615
11654
|
function normalizeUrl(raw) {
|
|
11616
11655
|
try {
|
|
@@ -11626,7 +11665,8 @@ function hashCommand(command) {
|
|
|
11626
11665
|
return (0, import_node_crypto2.createHash)("sha256").update(command).digest("hex");
|
|
11627
11666
|
}
|
|
11628
11667
|
function normalizeFilePath(raw) {
|
|
11629
|
-
const
|
|
11668
|
+
const home = getHomeDir();
|
|
11669
|
+
const expanded = raw.startsWith("~/") || raw === "~" ? `${home}${raw.slice(1)}` : raw;
|
|
11630
11670
|
return (0, import_node_path3.normalize)(expanded);
|
|
11631
11671
|
}
|
|
11632
11672
|
|
|
@@ -11634,16 +11674,6 @@ function normalizeFilePath(raw) {
|
|
|
11634
11674
|
function emptyAllowlist() {
|
|
11635
11675
|
return { urls: {}, commands: {}, filePaths: {} };
|
|
11636
11676
|
}
|
|
11637
|
-
function addEntry(record, key, reason, originalVerdict) {
|
|
11638
|
-
record[key] = { addedAt: (/* @__PURE__ */ new Date()).toISOString(), reason, originalVerdict };
|
|
11639
|
-
}
|
|
11640
|
-
function removeEntry(record, key) {
|
|
11641
|
-
if (key in record) {
|
|
11642
|
-
delete record[key];
|
|
11643
|
-
return true;
|
|
11644
|
-
}
|
|
11645
|
-
return false;
|
|
11646
|
-
}
|
|
11647
11677
|
function parseEntries(raw) {
|
|
11648
11678
|
const entries = {};
|
|
11649
11679
|
for (const [key, entryData] of Object.entries(raw)) {
|
|
@@ -11696,29 +11726,6 @@ async function loadAllowlist(config, logger = nullLogger) {
|
|
|
11696
11726
|
filePaths
|
|
11697
11727
|
};
|
|
11698
11728
|
}
|
|
11699
|
-
async function saveAllowlist(allowlist, config, logger = nullLogger) {
|
|
11700
|
-
const path = resolvePath(config.path);
|
|
11701
|
-
const serializeEntries = (entries) => Object.fromEntries(Object.entries(entries).map(([key, entry]) => [
|
|
11702
|
-
key,
|
|
11703
|
-
{
|
|
11704
|
-
added_at: entry.addedAt,
|
|
11705
|
-
reason: entry.reason,
|
|
11706
|
-
original_verdict: entry.originalVerdict
|
|
11707
|
-
}
|
|
11708
|
-
]));
|
|
11709
|
-
const data = {
|
|
11710
|
-
urls: serializeEntries(allowlist.urls),
|
|
11711
|
-
commands: serializeEntries(allowlist.commands),
|
|
11712
|
-
file_paths: serializeEntries(allowlist.filePaths)
|
|
11713
|
-
};
|
|
11714
|
-
try {
|
|
11715
|
-
await atomicWriteJson(path, data);
|
|
11716
|
-
return true;
|
|
11717
|
-
} catch (e) {
|
|
11718
|
-
logger.warn(`Failed to save allowlist to ${path}`, { error: String(e) });
|
|
11719
|
-
return false;
|
|
11720
|
-
}
|
|
11721
|
-
}
|
|
11722
11729
|
function isAllowlisted(allowlist, artifacts) {
|
|
11723
11730
|
for (const artifact of artifacts) {
|
|
11724
11731
|
if (artifact.type !== "command") {
|
|
@@ -11742,24 +11749,6 @@ function isAllowlisted(allowlist, artifacts) {
|
|
|
11742
11749
|
}
|
|
11743
11750
|
return false;
|
|
11744
11751
|
}
|
|
11745
|
-
function addUrl(allowlist, url, reason, originalVerdict) {
|
|
11746
|
-
addEntry(allowlist.urls, normalizeUrl(url), reason, originalVerdict);
|
|
11747
|
-
}
|
|
11748
|
-
function addCommand(allowlist, command, reason, originalVerdict) {
|
|
11749
|
-
addEntry(allowlist.commands, hashCommand(command), reason, originalVerdict);
|
|
11750
|
-
}
|
|
11751
|
-
function addFilePath(allowlist, filePath, reason, originalVerdict) {
|
|
11752
|
-
addEntry(allowlist.filePaths, normalizeFilePath(filePath), reason, originalVerdict);
|
|
11753
|
-
}
|
|
11754
|
-
function removeFilePath(allowlist, filePath) {
|
|
11755
|
-
return removeEntry(allowlist.filePaths, normalizeFilePath(filePath));
|
|
11756
|
-
}
|
|
11757
|
-
function removeUrl(allowlist, url) {
|
|
11758
|
-
return removeEntry(allowlist.urls, normalizeUrl(url));
|
|
11759
|
-
}
|
|
11760
|
-
function removeCommand(allowlist, commandHash) {
|
|
11761
|
-
return removeEntry(allowlist.commands, commandHash);
|
|
11762
|
-
}
|
|
11763
11752
|
|
|
11764
11753
|
// ../core/dist/approval-store.js
|
|
11765
11754
|
var import_node_crypto3 = require("node:crypto");
|
|
@@ -11853,6 +11842,7 @@ var ApprovalStore = class _ApprovalStore {
|
|
|
11853
11842
|
};
|
|
11854
11843
|
|
|
11855
11844
|
// ../core/dist/audit-log.js
|
|
11845
|
+
var import_node_crypto4 = require("node:crypto");
|
|
11856
11846
|
var import_promises = require("node:fs/promises");
|
|
11857
11847
|
var import_node_path4 = require("node:path");
|
|
11858
11848
|
var MAX_SUMMARY_LEN = 200;
|
|
@@ -11902,15 +11892,19 @@ async function appendEntry(config, entry) {
|
|
|
11902
11892
|
await (0, import_promises.appendFile)(path, `${JSON.stringify(entry)}
|
|
11903
11893
|
`);
|
|
11904
11894
|
}
|
|
11905
|
-
async function logVerdict(config, sessionId, toolName, toolInput, verdict, userOverride = false) {
|
|
11895
|
+
async function logVerdict(config, sessionId, toolName, toolInput, verdict, userOverride = false, conversationId, agentRuntime, hookType, signals) {
|
|
11906
11896
|
if (!config.enabled)
|
|
11907
11897
|
return;
|
|
11908
11898
|
if (verdict.decision === "allow" && !config.log_clean && !userOverride)
|
|
11909
11899
|
return;
|
|
11910
11900
|
const entry = {
|
|
11911
11901
|
type: "runtime_verdict",
|
|
11902
|
+
entry_id: (0, import_node_crypto4.randomUUID)(),
|
|
11912
11903
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11913
11904
|
session_id: sessionId,
|
|
11905
|
+
conversation_id: conversationId ?? sessionId,
|
|
11906
|
+
agent_runtime: agentRuntime,
|
|
11907
|
+
hook_type: hookType,
|
|
11914
11908
|
tool_name: toolName,
|
|
11915
11909
|
tool_input_summary: toolInputSummary(toolName, toolInput),
|
|
11916
11910
|
artifacts: verdict.artifacts,
|
|
@@ -11918,7 +11912,8 @@ async function logVerdict(config, sessionId, toolName, toolInput, verdict, userO
|
|
|
11918
11912
|
severity: verdict.severity,
|
|
11919
11913
|
reasons: verdict.reasons,
|
|
11920
11914
|
source: verdict.source,
|
|
11921
|
-
user_override: userOverride
|
|
11915
|
+
user_override: userOverride,
|
|
11916
|
+
signals
|
|
11922
11917
|
};
|
|
11923
11918
|
try {
|
|
11924
11919
|
await appendEntry(config, entry);
|
|
@@ -11951,10 +11946,12 @@ var VerdictCache = class {
|
|
|
11951
11946
|
path;
|
|
11952
11947
|
config;
|
|
11953
11948
|
logger;
|
|
11954
|
-
|
|
11949
|
+
version;
|
|
11950
|
+
constructor(config, logger = nullLogger, version) {
|
|
11955
11951
|
this.config = config;
|
|
11956
11952
|
this.logger = logger;
|
|
11957
11953
|
this.path = resolvePath(config.path);
|
|
11954
|
+
this.version = version;
|
|
11958
11955
|
}
|
|
11959
11956
|
async load() {
|
|
11960
11957
|
if (!this.config.enabled)
|
|
@@ -11976,12 +11973,8 @@ var VerdictCache = class {
|
|
|
11976
11973
|
return null;
|
|
11977
11974
|
const key = normalizeUrl(url);
|
|
11978
11975
|
const entry = this.store.urls[key];
|
|
11979
|
-
if (!entry)
|
|
11976
|
+
if (!entry || this.isStale(entry))
|
|
11980
11977
|
return null;
|
|
11981
|
-
if (new Date(entry.expiresAt).getTime() <= Date.now()) {
|
|
11982
|
-
delete this.store.urls[key];
|
|
11983
|
-
return null;
|
|
11984
|
-
}
|
|
11985
11978
|
return {
|
|
11986
11979
|
verdict: entry.verdict,
|
|
11987
11980
|
severity: entry.severity,
|
|
@@ -11999,19 +11992,16 @@ var VerdictCache = class {
|
|
|
11999
11992
|
this.store.urls[key] = {
|
|
12000
11993
|
...verdict,
|
|
12001
11994
|
checkedAt: now.toISOString(),
|
|
12002
|
-
expiresAt: expiresAt.toISOString()
|
|
11995
|
+
expiresAt: expiresAt.toISOString(),
|
|
11996
|
+
sageVersion: this.version
|
|
12003
11997
|
};
|
|
12004
11998
|
}
|
|
12005
11999
|
getCommand(commandHash) {
|
|
12006
12000
|
if (!this.config.enabled)
|
|
12007
12001
|
return null;
|
|
12008
12002
|
const entry = this.store.commands[commandHash];
|
|
12009
|
-
if (!entry)
|
|
12010
|
-
return null;
|
|
12011
|
-
if (new Date(entry.expiresAt).getTime() <= Date.now()) {
|
|
12012
|
-
delete this.store.commands[commandHash];
|
|
12003
|
+
if (!entry || this.isStale(entry))
|
|
12013
12004
|
return null;
|
|
12014
|
-
}
|
|
12015
12005
|
return {
|
|
12016
12006
|
verdict: entry.verdict,
|
|
12017
12007
|
severity: entry.severity,
|
|
@@ -12026,19 +12016,16 @@ var VerdictCache = class {
|
|
|
12026
12016
|
this.store.commands[commandHash] = {
|
|
12027
12017
|
...verdict,
|
|
12028
12018
|
checkedAt: now.toISOString(),
|
|
12029
|
-
expiresAt: FAR_FUTURE
|
|
12019
|
+
expiresAt: FAR_FUTURE,
|
|
12020
|
+
sageVersion: this.version
|
|
12030
12021
|
};
|
|
12031
12022
|
}
|
|
12032
12023
|
getPackage(key) {
|
|
12033
12024
|
if (!this.config.enabled)
|
|
12034
12025
|
return null;
|
|
12035
12026
|
const entry = this.store.packages[key];
|
|
12036
|
-
if (!entry)
|
|
12027
|
+
if (!entry || this.isStale(entry))
|
|
12037
12028
|
return null;
|
|
12038
|
-
if (new Date(entry.expiresAt).getTime() <= Date.now()) {
|
|
12039
|
-
delete this.store.packages[key];
|
|
12040
|
-
return null;
|
|
12041
|
-
}
|
|
12042
12029
|
return {
|
|
12043
12030
|
verdict: entry.verdict,
|
|
12044
12031
|
severity: entry.severity,
|
|
@@ -12055,9 +12042,17 @@ var VerdictCache = class {
|
|
|
12055
12042
|
this.store.packages[key] = {
|
|
12056
12043
|
...verdict,
|
|
12057
12044
|
checkedAt: now.toISOString(),
|
|
12058
|
-
expiresAt: expiresAt.toISOString()
|
|
12045
|
+
expiresAt: expiresAt.toISOString(),
|
|
12046
|
+
sageVersion: this.version
|
|
12059
12047
|
};
|
|
12060
12048
|
}
|
|
12049
|
+
isStale(entry) {
|
|
12050
|
+
if (new Date(entry.expiresAt).getTime() <= Date.now())
|
|
12051
|
+
return true;
|
|
12052
|
+
if (this.version && this.version !== "dev" && entry.sageVersion !== this.version)
|
|
12053
|
+
return true;
|
|
12054
|
+
return false;
|
|
12055
|
+
}
|
|
12061
12056
|
computePackageTtl(verdict, packageAgeDays) {
|
|
12062
12057
|
const isFresh = packageAgeDays !== null && packageAgeDays < 7;
|
|
12063
12058
|
switch (verdict) {
|
|
@@ -12073,15 +12068,37 @@ var VerdictCache = class {
|
|
|
12073
12068
|
if (!this.config.enabled)
|
|
12074
12069
|
return;
|
|
12075
12070
|
try {
|
|
12071
|
+
this.pruneStaleEntries();
|
|
12076
12072
|
await atomicWriteJson(this.path, this.store);
|
|
12077
12073
|
} catch (e) {
|
|
12078
12074
|
this.logger.warn(`Failed to save cache to ${this.path}`, { error: String(e) });
|
|
12079
12075
|
}
|
|
12080
12076
|
}
|
|
12077
|
+
pruneStaleEntries() {
|
|
12078
|
+
for (const [key, entry] of Object.entries(this.store.urls)) {
|
|
12079
|
+
if (this.isStale(entry))
|
|
12080
|
+
delete this.store.urls[key];
|
|
12081
|
+
}
|
|
12082
|
+
for (const [key, entry] of Object.entries(this.store.commands)) {
|
|
12083
|
+
if (this.isStale(entry))
|
|
12084
|
+
delete this.store.commands[key];
|
|
12085
|
+
}
|
|
12086
|
+
for (const [key, entry] of Object.entries(this.store.packages)) {
|
|
12087
|
+
if (this.isStale(entry))
|
|
12088
|
+
delete this.store.packages[key];
|
|
12089
|
+
}
|
|
12090
|
+
}
|
|
12081
12091
|
};
|
|
12082
12092
|
|
|
12093
|
+
// ../core/dist/clients/amsi-spawn.js
|
|
12094
|
+
var chldproc1 = "node:child_";
|
|
12095
|
+
var chldproc2 = "process";
|
|
12096
|
+
var chldproc = require(chldproc1 + chldproc2);
|
|
12097
|
+
function spawn(command, args, options) {
|
|
12098
|
+
return chldproc.spawn(command, args, options);
|
|
12099
|
+
}
|
|
12100
|
+
|
|
12083
12101
|
// ../core/dist/clients/amsi.js
|
|
12084
|
-
var import_node_child_process = require("node:child_process");
|
|
12085
12102
|
var AMSI_RESULT_DETECTED = 32768;
|
|
12086
12103
|
var AMSI_RESULT_BLOCKED_BY_ADMIN_START = 16384;
|
|
12087
12104
|
var MAX_SCAN_LENGTH = 1048576;
|
|
@@ -12089,7 +12106,7 @@ var PS_TIMEOUT = 15e3;
|
|
|
12089
12106
|
function isWSL() {
|
|
12090
12107
|
if (process.platform !== "linux")
|
|
12091
12108
|
return false;
|
|
12092
|
-
return !!process
|
|
12109
|
+
return !!process["env"]["WSL_DISTRO_NAME"];
|
|
12093
12110
|
}
|
|
12094
12111
|
function isAmsiSupported() {
|
|
12095
12112
|
return process.platform === "win32" || isWSL();
|
|
@@ -12340,7 +12357,7 @@ var PersistentPowershellAmsiBackend = class {
|
|
|
12340
12357
|
}
|
|
12341
12358
|
async init() {
|
|
12342
12359
|
try {
|
|
12343
|
-
this.process =
|
|
12360
|
+
this.process = spawn("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", PS_PERSISTENT_SCRIPT], {
|
|
12344
12361
|
stdio: ["pipe", "pipe", "pipe"],
|
|
12345
12362
|
windowsHide: true
|
|
12346
12363
|
});
|
|
@@ -12464,7 +12481,7 @@ var WslPowershellAmsiBackend = class {
|
|
|
12464
12481
|
const truncated = content.length > MAX_SCAN_LENGTH ? content.slice(0, MAX_SCAN_LENGTH) : content;
|
|
12465
12482
|
return new Promise((resolve3) => {
|
|
12466
12483
|
try {
|
|
12467
|
-
const ps =
|
|
12484
|
+
const ps = spawn("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", PS_ONESHOT_SCRIPT], { stdio: ["pipe", "pipe", "pipe"] });
|
|
12468
12485
|
let stdout = "";
|
|
12469
12486
|
const timer = setTimeout(() => {
|
|
12470
12487
|
this.logger.warn("AMSI: PowerShell one-shot timeout", { contentName });
|
|
@@ -12556,7 +12573,22 @@ var AmsiClient = class {
|
|
|
12556
12573
|
};
|
|
12557
12574
|
|
|
12558
12575
|
// ../core/dist/version.js
|
|
12559
|
-
var
|
|
12576
|
+
var import_node_path5 = require("node:path");
|
|
12577
|
+
var import_node_url = require("node:url");
|
|
12578
|
+
var import_meta = {};
|
|
12579
|
+
function resolveVersion() {
|
|
12580
|
+
if (true)
|
|
12581
|
+
return "0.7.0";
|
|
12582
|
+
try {
|
|
12583
|
+
const pkgPath = (0, import_node_path5.join)((0, import_node_path5.dirname)((0, import_node_url.fileURLToPath)(import_meta.url)), "..", "package.json");
|
|
12584
|
+
const pkg = JSON.parse(getFileContentSync(pkgPath));
|
|
12585
|
+
if (typeof pkg.version === "string")
|
|
12586
|
+
return pkg.version;
|
|
12587
|
+
} catch {
|
|
12588
|
+
}
|
|
12589
|
+
return "dev";
|
|
12590
|
+
}
|
|
12591
|
+
var VERSION = resolveVersion();
|
|
12560
12592
|
|
|
12561
12593
|
// ../core/dist/clients/file-check.js
|
|
12562
12594
|
var DEFAULT_TIMEOUT = 5;
|
|
@@ -12927,9 +12959,13 @@ var UrlCheckClient = class {
|
|
|
12927
12959
|
const result = answer.result ?? {};
|
|
12928
12960
|
const success = result.success ?? {};
|
|
12929
12961
|
const classification = success.classification ?? {};
|
|
12962
|
+
const detectionInfos = classification["detection-infos"] ?? [];
|
|
12930
12963
|
const classResult = classification.result ?? {};
|
|
12931
12964
|
const flags = success.flags ?? [];
|
|
12932
12965
|
const malicious = classResult.malicious;
|
|
12966
|
+
const detections = detectionInfos.filter((info) => typeof info.name === "string").map((info) => {
|
|
12967
|
+
return info.name;
|
|
12968
|
+
});
|
|
12933
12969
|
const findings = malicious ? (malicious.findings ?? []).map((f) => ({
|
|
12934
12970
|
severityName: f["severity-name"] ?? "unknown",
|
|
12935
12971
|
typeName: f["type-name"] ?? "unknown"
|
|
@@ -12937,6 +12973,7 @@ var UrlCheckClient = class {
|
|
|
12937
12973
|
return {
|
|
12938
12974
|
url,
|
|
12939
12975
|
isMalicious: Boolean(malicious),
|
|
12976
|
+
detections,
|
|
12940
12977
|
findings,
|
|
12941
12978
|
flags
|
|
12942
12979
|
};
|
|
@@ -13146,6 +13183,405 @@ var DecisionEngine = class {
|
|
|
13146
13183
|
}
|
|
13147
13184
|
};
|
|
13148
13185
|
|
|
13186
|
+
// ../core/dist/exceptions.js
|
|
13187
|
+
var import_node_crypto5 = require("node:crypto");
|
|
13188
|
+
var import_node_path7 = require("node:path");
|
|
13189
|
+
|
|
13190
|
+
// ../core/dist/trusted-domains.js
|
|
13191
|
+
var import_promises2 = require("node:fs/promises");
|
|
13192
|
+
var import_node_path6 = require("node:path");
|
|
13193
|
+
var import_yaml = __toESM(require_dist(), 1);
|
|
13194
|
+
async function loadTrustedDomains(allowlistsDir, logger = nullLogger) {
|
|
13195
|
+
let files;
|
|
13196
|
+
try {
|
|
13197
|
+
files = (await (0, import_promises2.readdir)(allowlistsDir)).filter((f) => f.endsWith(".yaml")).sort();
|
|
13198
|
+
} catch {
|
|
13199
|
+
logger.debug("Allowlists directory does not exist", { path: allowlistsDir });
|
|
13200
|
+
return [];
|
|
13201
|
+
}
|
|
13202
|
+
const domains = [];
|
|
13203
|
+
for (const filename of files) {
|
|
13204
|
+
const filePath = (0, import_node_path6.join)(allowlistsDir, filename);
|
|
13205
|
+
let content;
|
|
13206
|
+
try {
|
|
13207
|
+
content = await getFileContent(filePath);
|
|
13208
|
+
} catch (e) {
|
|
13209
|
+
logger.warn(`Failed to read ${filename}`, { error: String(e) });
|
|
13210
|
+
continue;
|
|
13211
|
+
}
|
|
13212
|
+
let data;
|
|
13213
|
+
try {
|
|
13214
|
+
data = (0, import_yaml.parse)(content);
|
|
13215
|
+
} catch (e) {
|
|
13216
|
+
logger.warn(`Failed to parse ${filename}`, { error: String(e) });
|
|
13217
|
+
continue;
|
|
13218
|
+
}
|
|
13219
|
+
if (!Array.isArray(data)) {
|
|
13220
|
+
logger.warn(`Expected list in ${filename}, got ${typeof data}`);
|
|
13221
|
+
continue;
|
|
13222
|
+
}
|
|
13223
|
+
for (const entry of data) {
|
|
13224
|
+
if (typeof entry !== "object" || entry === null) {
|
|
13225
|
+
logger.warn(`Skipping non-object entry in ${filename}`);
|
|
13226
|
+
continue;
|
|
13227
|
+
}
|
|
13228
|
+
const record = entry;
|
|
13229
|
+
const domain = record.domain;
|
|
13230
|
+
const reason = record.reason;
|
|
13231
|
+
if (!domain || typeof domain !== "string") {
|
|
13232
|
+
logger.warn(`Skipping entry without valid 'domain' in ${filename}`);
|
|
13233
|
+
continue;
|
|
13234
|
+
}
|
|
13235
|
+
if (!reason || typeof reason !== "string") {
|
|
13236
|
+
logger.warn(`Skipping entry without valid 'reason' in ${filename}`);
|
|
13237
|
+
continue;
|
|
13238
|
+
}
|
|
13239
|
+
domains.push({ domain: domain.toLowerCase(), reason });
|
|
13240
|
+
}
|
|
13241
|
+
}
|
|
13242
|
+
logger.debug(`Loaded ${domains.length} trusted domains from ${allowlistsDir}`);
|
|
13243
|
+
return domains;
|
|
13244
|
+
}
|
|
13245
|
+
function extractDomain(url) {
|
|
13246
|
+
try {
|
|
13247
|
+
const parsed = new URL(url);
|
|
13248
|
+
return parsed.hostname.toLowerCase() || null;
|
|
13249
|
+
} catch {
|
|
13250
|
+
return null;
|
|
13251
|
+
}
|
|
13252
|
+
}
|
|
13253
|
+
function isTrustedDomain(domain, trusted) {
|
|
13254
|
+
const domainLower = domain.toLowerCase();
|
|
13255
|
+
for (const td of trusted) {
|
|
13256
|
+
if (domainLower === td.domain)
|
|
13257
|
+
return true;
|
|
13258
|
+
if (domainLower.endsWith(`.${td.domain}`))
|
|
13259
|
+
return true;
|
|
13260
|
+
}
|
|
13261
|
+
return false;
|
|
13262
|
+
}
|
|
13263
|
+
|
|
13264
|
+
// ../core/dist/exceptions.js
|
|
13265
|
+
var MAX_RULES_WARNING = 100;
|
|
13266
|
+
var REGEX_TIMEOUT_MS = 50;
|
|
13267
|
+
function computeRuleId(decision, match, pattern) {
|
|
13268
|
+
const hash = (0, import_node_crypto5.createHash)("sha256").update(`${decision}:${match}:${pattern}`).digest("hex");
|
|
13269
|
+
return hash.slice(0, 8);
|
|
13270
|
+
}
|
|
13271
|
+
async function loadExceptions(config, logger = nullLogger) {
|
|
13272
|
+
const path = resolvePath(config.path);
|
|
13273
|
+
let raw;
|
|
13274
|
+
try {
|
|
13275
|
+
raw = await getFileContent(path);
|
|
13276
|
+
} catch {
|
|
13277
|
+
return [];
|
|
13278
|
+
}
|
|
13279
|
+
let data;
|
|
13280
|
+
try {
|
|
13281
|
+
data = JSON.parse(raw);
|
|
13282
|
+
} catch (e) {
|
|
13283
|
+
logger.warn(`Failed to parse exceptions from ${path}`, { error: String(e) });
|
|
13284
|
+
return [];
|
|
13285
|
+
}
|
|
13286
|
+
let parsed;
|
|
13287
|
+
try {
|
|
13288
|
+
parsed = ExceptionsFileSchema.parse(data);
|
|
13289
|
+
} catch (e) {
|
|
13290
|
+
logger.warn(`Exceptions validation failed for ${path}`, { error: String(e) });
|
|
13291
|
+
return [];
|
|
13292
|
+
}
|
|
13293
|
+
if (parsed.rules.length > MAX_RULES_WARNING) {
|
|
13294
|
+
logger.warn(`Exceptions file has ${parsed.rules.length} rules (>${MAX_RULES_WARNING})`, {
|
|
13295
|
+
path
|
|
13296
|
+
});
|
|
13297
|
+
}
|
|
13298
|
+
const seen = /* @__PURE__ */ new Map();
|
|
13299
|
+
let needsWrite = false;
|
|
13300
|
+
for (const rule of parsed.rules) {
|
|
13301
|
+
const expectedId = computeRuleId(rule.decision, rule.match, rule.pattern);
|
|
13302
|
+
if (rule.id !== expectedId) {
|
|
13303
|
+
needsWrite = true;
|
|
13304
|
+
}
|
|
13305
|
+
if (seen.has(expectedId)) {
|
|
13306
|
+
needsWrite = true;
|
|
13307
|
+
continue;
|
|
13308
|
+
}
|
|
13309
|
+
const normalized = {
|
|
13310
|
+
id: expectedId,
|
|
13311
|
+
decision: rule.decision,
|
|
13312
|
+
match: rule.match,
|
|
13313
|
+
pattern: rule.pattern,
|
|
13314
|
+
...rule.reason !== void 0 ? { reason: rule.reason } : {}
|
|
13315
|
+
};
|
|
13316
|
+
seen.set(expectedId, normalized);
|
|
13317
|
+
}
|
|
13318
|
+
const rules = [...seen.values()];
|
|
13319
|
+
if (needsWrite) {
|
|
13320
|
+
try {
|
|
13321
|
+
await atomicWriteJson(path, {
|
|
13322
|
+
rules: rules.map((r) => ({
|
|
13323
|
+
id: r.id,
|
|
13324
|
+
decision: r.decision,
|
|
13325
|
+
match: r.match,
|
|
13326
|
+
pattern: r.pattern,
|
|
13327
|
+
...r.reason !== void 0 ? { reason: r.reason } : {}
|
|
13328
|
+
}))
|
|
13329
|
+
});
|
|
13330
|
+
} catch (e) {
|
|
13331
|
+
logger.warn(`Failed to write normalized exceptions to ${path}`, { error: String(e) });
|
|
13332
|
+
}
|
|
13333
|
+
}
|
|
13334
|
+
for (const rule of rules) {
|
|
13335
|
+
if (rule.match === "regex") {
|
|
13336
|
+
try {
|
|
13337
|
+
new RegExp(rule.pattern);
|
|
13338
|
+
} catch (e) {
|
|
13339
|
+
logger.warn(`Invalid regex in exception rule ${rule.id}: ${rule.pattern}`, {
|
|
13340
|
+
error: String(e)
|
|
13341
|
+
});
|
|
13342
|
+
}
|
|
13343
|
+
}
|
|
13344
|
+
}
|
|
13345
|
+
return rules;
|
|
13346
|
+
}
|
|
13347
|
+
var UNSAFE_COMMAND_PATTERN = /[;&|`]|\$\(|<\(|>\(|\n|\r/;
|
|
13348
|
+
var SUDO_FLAGS_WITH_ARG = /* @__PURE__ */ new Set(["-u", "-g", "-C", "-D", "-R", "-T", "-h", "-p"]);
|
|
13349
|
+
function matchesExecutable(pattern, command) {
|
|
13350
|
+
if (UNSAFE_COMMAND_PATTERN.test(command)) {
|
|
13351
|
+
return false;
|
|
13352
|
+
}
|
|
13353
|
+
const tokens = command.trim().split(/\s+/);
|
|
13354
|
+
if (tokens.length === 0)
|
|
13355
|
+
return false;
|
|
13356
|
+
let i = 0;
|
|
13357
|
+
if (tokens[i] === "sudo") {
|
|
13358
|
+
i++;
|
|
13359
|
+
while (i < tokens.length) {
|
|
13360
|
+
const token = tokens[i];
|
|
13361
|
+
if (token === void 0 || token === "--") {
|
|
13362
|
+
if (token === "--")
|
|
13363
|
+
i++;
|
|
13364
|
+
break;
|
|
13365
|
+
}
|
|
13366
|
+
if (!token.startsWith("-"))
|
|
13367
|
+
break;
|
|
13368
|
+
i++;
|
|
13369
|
+
const nextToken = tokens[i];
|
|
13370
|
+
if (SUDO_FLAGS_WITH_ARG.has(token) && nextToken !== void 0 && !nextToken.startsWith("-")) {
|
|
13371
|
+
i++;
|
|
13372
|
+
}
|
|
13373
|
+
}
|
|
13374
|
+
}
|
|
13375
|
+
if (i < tokens.length && tokens[i] === "env") {
|
|
13376
|
+
i++;
|
|
13377
|
+
while (i < tokens.length) {
|
|
13378
|
+
const tok = tokens[i];
|
|
13379
|
+
if (tok === void 0 || !/^\w+=/.test(tok))
|
|
13380
|
+
break;
|
|
13381
|
+
i++;
|
|
13382
|
+
}
|
|
13383
|
+
}
|
|
13384
|
+
const exeToken = tokens[i];
|
|
13385
|
+
if (exeToken === void 0)
|
|
13386
|
+
return false;
|
|
13387
|
+
const exeParts = exeToken.split("/");
|
|
13388
|
+
const exeName = exeParts[exeParts.length - 1];
|
|
13389
|
+
if (!exeName)
|
|
13390
|
+
return false;
|
|
13391
|
+
const patternTokens = pattern.trim().split(/\s+/);
|
|
13392
|
+
if (patternTokens.length === 0)
|
|
13393
|
+
return false;
|
|
13394
|
+
if (exeName !== patternTokens[0])
|
|
13395
|
+
return false;
|
|
13396
|
+
for (let j = 1; j < patternTokens.length; j++) {
|
|
13397
|
+
if (i + j >= tokens.length)
|
|
13398
|
+
return false;
|
|
13399
|
+
if (tokens[i + j] !== patternTokens[j])
|
|
13400
|
+
return false;
|
|
13401
|
+
}
|
|
13402
|
+
return true;
|
|
13403
|
+
}
|
|
13404
|
+
function matchesDomain(pattern, url) {
|
|
13405
|
+
const domain = extractDomain(url);
|
|
13406
|
+
if (!domain)
|
|
13407
|
+
return false;
|
|
13408
|
+
const portMatch = pattern.match(/^(.+):(\d+)$/);
|
|
13409
|
+
const portDomain = portMatch?.[1];
|
|
13410
|
+
const portPort = portMatch?.[2];
|
|
13411
|
+
if (portDomain && portPort) {
|
|
13412
|
+
const patternDomain = portDomain.toLowerCase();
|
|
13413
|
+
const patternPort = portPort;
|
|
13414
|
+
let urlPort;
|
|
13415
|
+
try {
|
|
13416
|
+
const parsed = new URL(url);
|
|
13417
|
+
urlPort = parsed.port || (parsed.protocol === "https:" ? "443" : "80");
|
|
13418
|
+
} catch {
|
|
13419
|
+
return false;
|
|
13420
|
+
}
|
|
13421
|
+
if (urlPort !== patternPort)
|
|
13422
|
+
return false;
|
|
13423
|
+
return isTrustedDomain(domain, [{ domain: patternDomain, reason: "" }]);
|
|
13424
|
+
}
|
|
13425
|
+
return isTrustedDomain(domain, [{ domain: pattern.toLowerCase(), reason: "" }]);
|
|
13426
|
+
}
|
|
13427
|
+
function normalizePatternPath(pattern) {
|
|
13428
|
+
const home = getHomeDir();
|
|
13429
|
+
const expanded = pattern.startsWith("~/") || pattern === "~" ? `${home}${pattern.slice(1)}` : pattern;
|
|
13430
|
+
return (0, import_node_path7.normalize)(expanded);
|
|
13431
|
+
}
|
|
13432
|
+
function matchesPath(pattern, filePath) {
|
|
13433
|
+
const hasWildcard = pattern.includes("*");
|
|
13434
|
+
if (hasWildcard) {
|
|
13435
|
+
return globMatch(normalizePatternPath(pattern), normalizePatternPath(filePath));
|
|
13436
|
+
}
|
|
13437
|
+
const normalizedPattern = normalizePatternPath(pattern);
|
|
13438
|
+
const normalizedPath = normalizePatternPath(filePath);
|
|
13439
|
+
if (normalizedPath === normalizedPattern)
|
|
13440
|
+
return true;
|
|
13441
|
+
if (normalizedPath.startsWith(normalizedPattern + import_node_path7.sep))
|
|
13442
|
+
return true;
|
|
13443
|
+
return false;
|
|
13444
|
+
}
|
|
13445
|
+
function matchesPlugin(pattern, pluginKey) {
|
|
13446
|
+
const hasWildcard = pattern.includes("*");
|
|
13447
|
+
if (hasWildcard) {
|
|
13448
|
+
return globMatch(pattern, pluginKey);
|
|
13449
|
+
}
|
|
13450
|
+
if (pluginKey === pattern)
|
|
13451
|
+
return true;
|
|
13452
|
+
if (pluginKey.startsWith(pattern) && pluginKey[pattern.length] === "@")
|
|
13453
|
+
return true;
|
|
13454
|
+
return false;
|
|
13455
|
+
}
|
|
13456
|
+
function matchesRegex(pattern, value) {
|
|
13457
|
+
let re;
|
|
13458
|
+
try {
|
|
13459
|
+
re = new RegExp(pattern);
|
|
13460
|
+
} catch {
|
|
13461
|
+
return false;
|
|
13462
|
+
}
|
|
13463
|
+
const start = performance.now();
|
|
13464
|
+
try {
|
|
13465
|
+
const result = re.test(value);
|
|
13466
|
+
if (performance.now() - start > REGEX_TIMEOUT_MS) {
|
|
13467
|
+
return false;
|
|
13468
|
+
}
|
|
13469
|
+
return result;
|
|
13470
|
+
} catch {
|
|
13471
|
+
return false;
|
|
13472
|
+
}
|
|
13473
|
+
}
|
|
13474
|
+
function globMatch(pattern, value) {
|
|
13475
|
+
const normPattern = pattern.replace(/\\/g, "/");
|
|
13476
|
+
const normValue = value.replace(/\\/g, "/");
|
|
13477
|
+
const regexStr = globToRegex(normPattern);
|
|
13478
|
+
try {
|
|
13479
|
+
const re = new RegExp(`^${regexStr}$`);
|
|
13480
|
+
return re.test(normValue);
|
|
13481
|
+
} catch {
|
|
13482
|
+
return false;
|
|
13483
|
+
}
|
|
13484
|
+
}
|
|
13485
|
+
function globToRegex(pattern) {
|
|
13486
|
+
let result = "";
|
|
13487
|
+
let i = 0;
|
|
13488
|
+
while (i < pattern.length) {
|
|
13489
|
+
const ch = pattern[i];
|
|
13490
|
+
if (ch === void 0)
|
|
13491
|
+
break;
|
|
13492
|
+
if (ch === "*" && pattern[i + 1] === "*") {
|
|
13493
|
+
result += ".*";
|
|
13494
|
+
i += 2;
|
|
13495
|
+
if (i < pattern.length && pattern[i] === "/")
|
|
13496
|
+
i++;
|
|
13497
|
+
} else if (ch === "*") {
|
|
13498
|
+
result += "[^/]*";
|
|
13499
|
+
i++;
|
|
13500
|
+
} else if (ch === "?") {
|
|
13501
|
+
result += "[^/]";
|
|
13502
|
+
i++;
|
|
13503
|
+
} else {
|
|
13504
|
+
result += escapeRegex(ch);
|
|
13505
|
+
i++;
|
|
13506
|
+
}
|
|
13507
|
+
}
|
|
13508
|
+
return result;
|
|
13509
|
+
}
|
|
13510
|
+
function escapeRegex(char) {
|
|
13511
|
+
return char.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
13512
|
+
}
|
|
13513
|
+
function findDenyException(rules, artifacts) {
|
|
13514
|
+
const denyRules = rules.filter((r) => r.decision === "deny");
|
|
13515
|
+
for (const artifact of artifacts) {
|
|
13516
|
+
for (const rule of denyRules) {
|
|
13517
|
+
if (matchesArtifact(rule, artifact)) {
|
|
13518
|
+
return { rule, artifact };
|
|
13519
|
+
}
|
|
13520
|
+
}
|
|
13521
|
+
}
|
|
13522
|
+
return null;
|
|
13523
|
+
}
|
|
13524
|
+
function findAllowException(rules, artifacts) {
|
|
13525
|
+
const allowRules = rules.filter((r) => r.decision === "allow");
|
|
13526
|
+
const execRules = allowRules.filter((r) => r.match === "executable");
|
|
13527
|
+
if (execRules.length > 0) {
|
|
13528
|
+
for (const artifact of artifacts) {
|
|
13529
|
+
if (artifact.type !== "command")
|
|
13530
|
+
continue;
|
|
13531
|
+
const match = execRules.find((r) => matchesExecutable(r.pattern, artifact.value));
|
|
13532
|
+
if (match)
|
|
13533
|
+
return match;
|
|
13534
|
+
}
|
|
13535
|
+
}
|
|
13536
|
+
const pathRules = allowRules.filter((r) => r.match === "path");
|
|
13537
|
+
if (pathRules.length > 0) {
|
|
13538
|
+
for (const artifact of artifacts) {
|
|
13539
|
+
if (artifact.type !== "file_path")
|
|
13540
|
+
continue;
|
|
13541
|
+
const match = pathRules.find((r) => matchesPath(r.pattern, artifact.value));
|
|
13542
|
+
if (match)
|
|
13543
|
+
return match;
|
|
13544
|
+
}
|
|
13545
|
+
}
|
|
13546
|
+
const domainRules = allowRules.filter((r) => r.match === "domain");
|
|
13547
|
+
const firstDomainRule = domainRules[0];
|
|
13548
|
+
if (firstDomainRule && artifacts.length > 0 && artifacts.every((a) => a.type === "url")) {
|
|
13549
|
+
if (artifacts.every((a) => domainRules.some((r) => matchesDomain(r.pattern, a.value)))) {
|
|
13550
|
+
return firstDomainRule;
|
|
13551
|
+
}
|
|
13552
|
+
}
|
|
13553
|
+
const regexRules = allowRules.filter((r) => r.match === "regex");
|
|
13554
|
+
const firstRegexRule = regexRules[0];
|
|
13555
|
+
if (firstRegexRule && artifacts.length > 0) {
|
|
13556
|
+
if (artifacts.every((a) => regexRules.some((r) => matchesRegex(r.pattern, a.value)))) {
|
|
13557
|
+
return firstRegexRule;
|
|
13558
|
+
}
|
|
13559
|
+
}
|
|
13560
|
+
return null;
|
|
13561
|
+
}
|
|
13562
|
+
function findPluginDenyException(rules, pluginKey) {
|
|
13563
|
+
const found = rules.find((r) => r.decision === "deny" && r.match === "plugin" && matchesPlugin(r.pattern, pluginKey));
|
|
13564
|
+
return found ?? null;
|
|
13565
|
+
}
|
|
13566
|
+
function findPluginAllowException(rules, pluginKey) {
|
|
13567
|
+
const found = rules.find((r) => r.decision === "allow" && r.match === "plugin" && matchesPlugin(r.pattern, pluginKey));
|
|
13568
|
+
return found ?? null;
|
|
13569
|
+
}
|
|
13570
|
+
function matchesArtifact(rule, artifact) {
|
|
13571
|
+
switch (rule.match) {
|
|
13572
|
+
case "executable":
|
|
13573
|
+
return artifact.type === "command" && matchesExecutable(rule.pattern, artifact.value);
|
|
13574
|
+
case "domain":
|
|
13575
|
+
return artifact.type === "url" && matchesDomain(rule.pattern, artifact.value);
|
|
13576
|
+
case "path":
|
|
13577
|
+
return artifact.type === "file_path" && matchesPath(rule.pattern, artifact.value);
|
|
13578
|
+
case "regex":
|
|
13579
|
+
return matchesRegex(rule.pattern, artifact.value);
|
|
13580
|
+
case "plugin":
|
|
13581
|
+
return false;
|
|
13582
|
+
}
|
|
13583
|
+
}
|
|
13584
|
+
|
|
13149
13585
|
// ../core/dist/extractors.js
|
|
13150
13586
|
var URL_PATTERN = /https?:\/\/[^\s"')<>[\]{}]+/g;
|
|
13151
13587
|
var TRAILING_PUNCT = /[.,;:!?]+$/;
|
|
@@ -13232,80 +13668,6 @@ function extractFromEdit(toolInput) {
|
|
|
13232
13668
|
return extractFileArtifacts(toolInput, "file_path", "new_string", "edit", "from_edit_content");
|
|
13233
13669
|
}
|
|
13234
13670
|
|
|
13235
|
-
// ../core/dist/trusted-domains.js
|
|
13236
|
-
var import_promises2 = require("node:fs/promises");
|
|
13237
|
-
var import_node_path5 = require("node:path");
|
|
13238
|
-
var import_yaml = __toESM(require_dist(), 1);
|
|
13239
|
-
async function loadTrustedDomains(allowlistsDir, logger = nullLogger) {
|
|
13240
|
-
let files;
|
|
13241
|
-
try {
|
|
13242
|
-
files = (await (0, import_promises2.readdir)(allowlistsDir)).filter((f) => f.endsWith(".yaml")).sort();
|
|
13243
|
-
} catch {
|
|
13244
|
-
logger.debug("Allowlists directory does not exist", { path: allowlistsDir });
|
|
13245
|
-
return [];
|
|
13246
|
-
}
|
|
13247
|
-
const domains = [];
|
|
13248
|
-
for (const filename of files) {
|
|
13249
|
-
const filePath = (0, import_node_path5.join)(allowlistsDir, filename);
|
|
13250
|
-
let content;
|
|
13251
|
-
try {
|
|
13252
|
-
content = await getFileContent(filePath);
|
|
13253
|
-
} catch (e) {
|
|
13254
|
-
logger.warn(`Failed to read ${filename}`, { error: String(e) });
|
|
13255
|
-
continue;
|
|
13256
|
-
}
|
|
13257
|
-
let data;
|
|
13258
|
-
try {
|
|
13259
|
-
data = (0, import_yaml.parse)(content);
|
|
13260
|
-
} catch (e) {
|
|
13261
|
-
logger.warn(`Failed to parse ${filename}`, { error: String(e) });
|
|
13262
|
-
continue;
|
|
13263
|
-
}
|
|
13264
|
-
if (!Array.isArray(data)) {
|
|
13265
|
-
logger.warn(`Expected list in ${filename}, got ${typeof data}`);
|
|
13266
|
-
continue;
|
|
13267
|
-
}
|
|
13268
|
-
for (const entry of data) {
|
|
13269
|
-
if (typeof entry !== "object" || entry === null) {
|
|
13270
|
-
logger.warn(`Skipping non-object entry in ${filename}`);
|
|
13271
|
-
continue;
|
|
13272
|
-
}
|
|
13273
|
-
const record = entry;
|
|
13274
|
-
const domain = record.domain;
|
|
13275
|
-
const reason = record.reason;
|
|
13276
|
-
if (!domain || typeof domain !== "string") {
|
|
13277
|
-
logger.warn(`Skipping entry without valid 'domain' in ${filename}`);
|
|
13278
|
-
continue;
|
|
13279
|
-
}
|
|
13280
|
-
if (!reason || typeof reason !== "string") {
|
|
13281
|
-
logger.warn(`Skipping entry without valid 'reason' in ${filename}`);
|
|
13282
|
-
continue;
|
|
13283
|
-
}
|
|
13284
|
-
domains.push({ domain: domain.toLowerCase(), reason });
|
|
13285
|
-
}
|
|
13286
|
-
}
|
|
13287
|
-
logger.debug(`Loaded ${domains.length} trusted domains from ${allowlistsDir}`);
|
|
13288
|
-
return domains;
|
|
13289
|
-
}
|
|
13290
|
-
function extractDomain(url) {
|
|
13291
|
-
try {
|
|
13292
|
-
const parsed = new URL(url);
|
|
13293
|
-
return parsed.hostname.toLowerCase() || null;
|
|
13294
|
-
} catch {
|
|
13295
|
-
return null;
|
|
13296
|
-
}
|
|
13297
|
-
}
|
|
13298
|
-
function isTrustedDomain(domain, trusted) {
|
|
13299
|
-
const domainLower = domain.toLowerCase();
|
|
13300
|
-
for (const td of trusted) {
|
|
13301
|
-
if (domainLower === td.domain)
|
|
13302
|
-
return true;
|
|
13303
|
-
if (domainLower.endsWith(`.${td.domain}`))
|
|
13304
|
-
return true;
|
|
13305
|
-
}
|
|
13306
|
-
return false;
|
|
13307
|
-
}
|
|
13308
|
-
|
|
13309
13671
|
// ../core/dist/heuristics.js
|
|
13310
13672
|
var TRUSTED_DOMAIN_SUPPRESSIBLE = /* @__PURE__ */ new Set([
|
|
13311
13673
|
"CLT-CMD-001",
|
|
@@ -13448,7 +13810,9 @@ var PackageChecker = class {
|
|
|
13448
13810
|
verdict: "malicious",
|
|
13449
13811
|
confidence: 1,
|
|
13450
13812
|
details: `Malicious package '${pkg.name}' (${detections})`,
|
|
13451
|
-
fileCheckSeverity: sev
|
|
13813
|
+
fileCheckSeverity: sev,
|
|
13814
|
+
fileSha256: metadata.latestHash,
|
|
13815
|
+
fileDetectionNames: fileResult.detectionNames
|
|
13452
13816
|
};
|
|
13453
13817
|
}
|
|
13454
13818
|
}
|
|
@@ -13822,9 +14186,49 @@ function extractFromRequirementsTxt(content) {
|
|
|
13822
14186
|
return packages;
|
|
13823
14187
|
}
|
|
13824
14188
|
|
|
14189
|
+
// ../core/dist/statusline.js
|
|
14190
|
+
var import_node_path8 = require("node:path");
|
|
14191
|
+
var STATUS_PREFIX = "statusline-";
|
|
14192
|
+
var STATUS_SUFFIX = ".txt";
|
|
14193
|
+
function sanitizeSessionId(sessionId) {
|
|
14194
|
+
return sessionId.replace(/[^a-zA-Z0-9-]/g, "_");
|
|
14195
|
+
}
|
|
14196
|
+
function statusFilePath(sessionId) {
|
|
14197
|
+
return (0, import_node_path8.join)(resolvePath(SAGE_DIR), `${STATUS_PREFIX}${sanitizeSessionId(sessionId)}${STATUS_SUFFIX}`);
|
|
14198
|
+
}
|
|
14199
|
+
function emptyStatus() {
|
|
14200
|
+
return {
|
|
14201
|
+
denied: 0,
|
|
14202
|
+
flagged: 0,
|
|
14203
|
+
lastCategory: null,
|
|
14204
|
+
lastReason: null,
|
|
14205
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
14206
|
+
};
|
|
14207
|
+
}
|
|
14208
|
+
async function readStatus(sessionId) {
|
|
14209
|
+
try {
|
|
14210
|
+
const raw = await getFileContent(statusFilePath(sessionId), "utf-8");
|
|
14211
|
+
return JSON.parse(raw);
|
|
14212
|
+
} catch {
|
|
14213
|
+
return emptyStatus();
|
|
14214
|
+
}
|
|
14215
|
+
}
|
|
14216
|
+
async function updateSessionStatus(sessionId, verdict) {
|
|
14217
|
+
const status = await readStatus(sessionId);
|
|
14218
|
+
if (verdict.decision === "deny") {
|
|
14219
|
+
status.denied++;
|
|
14220
|
+
} else if (verdict.decision === "ask") {
|
|
14221
|
+
status.flagged++;
|
|
14222
|
+
}
|
|
14223
|
+
status.lastCategory = verdict.category;
|
|
14224
|
+
status.lastReason = verdict.reasons[0] ?? null;
|
|
14225
|
+
status.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
14226
|
+
await atomicWriteJson(statusFilePath(sessionId), status);
|
|
14227
|
+
}
|
|
14228
|
+
|
|
13825
14229
|
// ../core/dist/threat-loader.js
|
|
13826
14230
|
var import_promises3 = require("node:fs/promises");
|
|
13827
|
-
var
|
|
14231
|
+
var import_node_path9 = require("node:path");
|
|
13828
14232
|
var import_yaml2 = __toESM(require_dist(), 1);
|
|
13829
14233
|
var REQUIRED_FIELDS = /* @__PURE__ */ new Set([
|
|
13830
14234
|
"id",
|
|
@@ -13862,7 +14266,7 @@ async function loadThreats(threatDir, logger = nullLogger) {
|
|
|
13862
14266
|
return threats;
|
|
13863
14267
|
}
|
|
13864
14268
|
for (const filename of files) {
|
|
13865
|
-
const filePath = (0,
|
|
14269
|
+
const filePath = (0, import_node_path9.join)(threatDir, filename);
|
|
13866
14270
|
let content;
|
|
13867
14271
|
try {
|
|
13868
14272
|
content = await getFileContent(filePath);
|
|
@@ -13911,6 +14315,7 @@ async function loadThreats(threatDir, logger = nullLogger) {
|
|
|
13911
14315
|
const matchOn = new Set(Array.isArray(rawMatchOn) ? rawMatchOn : [rawMatchOn]);
|
|
13912
14316
|
threats.push({
|
|
13913
14317
|
id: record.id,
|
|
14318
|
+
version: typeof record.version === "number" ? record.version : void 0,
|
|
13914
14319
|
category: record.category,
|
|
13915
14320
|
severity: record.severity,
|
|
13916
14321
|
confidence: Number(record.confidence),
|
|
@@ -13948,17 +14353,50 @@ async function evaluateToolCall(request, context) {
|
|
|
13948
14353
|
return allowVerdict("no_artifacts");
|
|
13949
14354
|
}
|
|
13950
14355
|
try {
|
|
13951
|
-
const
|
|
13952
|
-
|
|
13953
|
-
|
|
13954
|
-
|
|
14356
|
+
const exceptions = await loadExceptions(config.exceptions, logger);
|
|
14357
|
+
const denyMatch = findDenyException(exceptions, request.artifacts);
|
|
14358
|
+
if (denyMatch) {
|
|
14359
|
+
const verdict2 = {
|
|
14360
|
+
decision: "deny",
|
|
14361
|
+
category: "exception",
|
|
14362
|
+
confidence: 1,
|
|
14363
|
+
severity: "critical",
|
|
14364
|
+
source: "exception",
|
|
14365
|
+
artifacts: [denyMatch.artifact.value],
|
|
14366
|
+
matchedThreatId: null,
|
|
14367
|
+
reasons: [
|
|
14368
|
+
`Deny exception: ${denyMatch.rule.match} pattern '${denyMatch.rule.pattern}'${denyMatch.rule.reason ? ` \u2014 ${denyMatch.rule.reason}` : ""}`
|
|
14369
|
+
]
|
|
14370
|
+
};
|
|
14371
|
+
try {
|
|
14372
|
+
await logVerdict(config.logging, request.sessionId, request.toolName, request.toolInput, verdict2, false, request.conversationId, request.agentRuntime, request.hookType, void 0);
|
|
14373
|
+
} catch {
|
|
14374
|
+
}
|
|
13955
14375
|
return verdict2;
|
|
13956
14376
|
}
|
|
14377
|
+
try {
|
|
14378
|
+
const allowlist = await loadAllowlist(config.allowlist, logger);
|
|
14379
|
+
if (isAllowlisted(allowlist, request.artifacts)) {
|
|
14380
|
+
const allowV = allowVerdict("allowlisted");
|
|
14381
|
+
await logVerdict(config.logging, request.sessionId, request.toolName, request.toolInput, allowV, true, request.conversationId, request.agentRuntime, request.hookType, void 0);
|
|
14382
|
+
return allowV;
|
|
14383
|
+
}
|
|
14384
|
+
} catch {
|
|
14385
|
+
}
|
|
14386
|
+
const allowMatch = findAllowException(exceptions, request.artifacts);
|
|
14387
|
+
if (allowMatch) {
|
|
14388
|
+
const allowV = allowVerdict("exception");
|
|
14389
|
+
try {
|
|
14390
|
+
await logVerdict(config.logging, request.sessionId, request.toolName, request.toolInput, allowV, true, request.conversationId, request.agentRuntime, request.hookType, void 0);
|
|
14391
|
+
} catch {
|
|
14392
|
+
}
|
|
14393
|
+
return allowV;
|
|
14394
|
+
}
|
|
13957
14395
|
} catch {
|
|
13958
14396
|
}
|
|
13959
14397
|
let cache = null;
|
|
13960
14398
|
try {
|
|
13961
|
-
cache = new VerdictCache(config.cache, logger);
|
|
14399
|
+
cache = new VerdictCache(config.cache, logger, VERSION);
|
|
13962
14400
|
await cache.load();
|
|
13963
14401
|
} catch {
|
|
13964
14402
|
cache = null;
|
|
@@ -14050,10 +14488,64 @@ async function evaluateToolCall(request, context) {
|
|
|
14050
14488
|
}
|
|
14051
14489
|
}
|
|
14052
14490
|
await cacheUrlResults(urlCheckResults, cache);
|
|
14491
|
+
function formatPackageDetectionName(p) {
|
|
14492
|
+
const base = `PKG|${p.verdict}|registry=${p.registry}|name=${p.packageName}`;
|
|
14493
|
+
if (p.verdict === "suspicious_age") {
|
|
14494
|
+
const ageDays = typeof p.ageDays === "number" ? Math.floor(p.ageDays) : void 0;
|
|
14495
|
+
return ageDays !== void 0 ? `${base}|age_days=${ageDays}` : base;
|
|
14496
|
+
}
|
|
14497
|
+
if (p.verdict === "malicious") {
|
|
14498
|
+
const det = (p.fileDetectionNames ?? []).filter((d) => typeof d === "string" && d.length > 0);
|
|
14499
|
+
return det.length > 0 ? `${base}|det=${det.join(",")}` : base;
|
|
14500
|
+
}
|
|
14501
|
+
return base;
|
|
14502
|
+
}
|
|
14503
|
+
const auditSignals = {};
|
|
14504
|
+
if (heuristicMatches.length > 0) {
|
|
14505
|
+
auditSignals.heuristics = heuristicMatches.map((m) => ({
|
|
14506
|
+
rule_id: m.threat.id,
|
|
14507
|
+
rule_version: typeof m.threat.version === "number" ? m.threat.version : void 0
|
|
14508
|
+
}));
|
|
14509
|
+
}
|
|
14510
|
+
if (urlCheckResults.length > 0) {
|
|
14511
|
+
const relevant = urlCheckResults.filter((r) => r.isMalicious || r.flags.length > 0);
|
|
14512
|
+
if (relevant.length > 0) {
|
|
14513
|
+
auditSignals.url_checks = relevant.flatMap((r) => {
|
|
14514
|
+
return r.detections.map((d) => ({
|
|
14515
|
+
detection_name: d,
|
|
14516
|
+
url: r.url
|
|
14517
|
+
}));
|
|
14518
|
+
});
|
|
14519
|
+
}
|
|
14520
|
+
}
|
|
14521
|
+
if (packageCheckResults.length > 0) {
|
|
14522
|
+
const relevant = packageCheckResults.filter((p) => p.verdict !== "clean");
|
|
14523
|
+
if (relevant.length > 0) {
|
|
14524
|
+
auditSignals.package_checks = relevant.map((p) => ({
|
|
14525
|
+
detection_name: formatPackageDetectionName(p),
|
|
14526
|
+
package_name: p.packageName,
|
|
14527
|
+
package_version: void 0,
|
|
14528
|
+
package_registry: p.registry
|
|
14529
|
+
}));
|
|
14530
|
+
}
|
|
14531
|
+
const fileRelevant = relevant.filter((p) => !!p.fileSha256 && (p.fileDetectionNames?.length ?? 0) > 0);
|
|
14532
|
+
if (fileRelevant.length > 0) {
|
|
14533
|
+
auditSignals.file_checks = fileRelevant.map((p) => ({
|
|
14534
|
+
detection_name: (p.fileDetectionNames ?? []).join(","),
|
|
14535
|
+
file_sha256: p.fileSha256
|
|
14536
|
+
}));
|
|
14537
|
+
}
|
|
14538
|
+
}
|
|
14053
14539
|
try {
|
|
14054
|
-
await logVerdict(config.logging, request.sessionId, request.toolName, request.toolInput, verdict);
|
|
14540
|
+
await logVerdict(config.logging, request.sessionId, request.toolName, request.toolInput, verdict, false, request.conversationId, request.agentRuntime, request.hookType, Object.keys(auditSignals).length > 0 ? auditSignals : void 0);
|
|
14055
14541
|
} catch {
|
|
14056
14542
|
}
|
|
14543
|
+
if (verdict.decision !== "allow") {
|
|
14544
|
+
try {
|
|
14545
|
+
await updateSessionStatus(request.sessionId, verdict);
|
|
14546
|
+
} catch {
|
|
14547
|
+
}
|
|
14548
|
+
}
|
|
14057
14549
|
return verdict;
|
|
14058
14550
|
}
|
|
14059
14551
|
function partitionUrlsByCache(urls, cache) {
|
|
@@ -14189,7 +14681,7 @@ function separatorLine(headerLength) {
|
|
|
14189
14681
|
return "\u2501".repeat(headerLength);
|
|
14190
14682
|
}
|
|
14191
14683
|
function formatUpdateNotice(result) {
|
|
14192
|
-
return `\u2B06\uFE0F Update available: v${result.currentVersion} \u2192 v${result.latestVersion} (https://github.com/
|
|
14684
|
+
return `\u2B06\uFE0F Update available: v${result.currentVersion} \u2192 v${result.latestVersion} (https://github.com/gendigitalinc/sage)`;
|
|
14193
14685
|
}
|
|
14194
14686
|
function formatStartupClean(version, versionCheck) {
|
|
14195
14687
|
const base = `\u{1F6E1}\uFE0F Sage v${version} by Gen Digital \u2705 No threats found`;
|
|
@@ -14311,79 +14803,17 @@ async function approveAction(store, actionId) {
|
|
|
14311
14803
|
"Inform the user that they can ask you to permanently allowlist this artifact. Do NOT add it to the allowlist unless the user explicitly requests it."
|
|
14312
14804
|
].join("\n");
|
|
14313
14805
|
}
|
|
14314
|
-
async function addToAllowlist(store, type, value, reason, configPath, logger = nullLogger) {
|
|
14315
|
-
if (!store.hasApprovedArtifact(type, value)) {
|
|
14316
|
-
return "Cannot add to allowlist: no recent user approval found for this artifact.";
|
|
14317
|
-
}
|
|
14318
|
-
const config = await loadConfig(configPath, logger);
|
|
14319
|
-
const allowlist = await loadAllowlist(config.allowlist, logger);
|
|
14320
|
-
const entryReason = reason ?? "Approved by user via sage_approve";
|
|
14321
|
-
if (type === "url") {
|
|
14322
|
-
addUrl(allowlist, value, entryReason, "ask");
|
|
14323
|
-
} else if (type === "command") {
|
|
14324
|
-
addCommand(allowlist, value, entryReason, "ask");
|
|
14325
|
-
} else {
|
|
14326
|
-
addFilePath(allowlist, value, entryReason, "ask");
|
|
14327
|
-
}
|
|
14328
|
-
const saved = await saveAllowlist(allowlist, config.allowlist, logger);
|
|
14329
|
-
if (!saved) {
|
|
14330
|
-
return "Failed to save allowlist. Approval preserved \u2014 retry is safe.";
|
|
14331
|
-
}
|
|
14332
|
-
store.consumeApprovedArtifact(type, value);
|
|
14333
|
-
return `Added ${type} to Sage allowlist.`;
|
|
14334
|
-
}
|
|
14335
|
-
async function removeFromAllowlist(type, value, configPath, logger = nullLogger) {
|
|
14336
|
-
const config = await loadConfig(configPath, logger);
|
|
14337
|
-
const allowlist = await loadAllowlist(config.allowlist, logger);
|
|
14338
|
-
let removed;
|
|
14339
|
-
let removedViaHash = false;
|
|
14340
|
-
switch (type) {
|
|
14341
|
-
case "url":
|
|
14342
|
-
removed = removeUrl(allowlist, value);
|
|
14343
|
-
break;
|
|
14344
|
-
case "command":
|
|
14345
|
-
removed = removeCommand(allowlist, value);
|
|
14346
|
-
if (!removed) {
|
|
14347
|
-
removed = removeCommand(allowlist, hashCommand(value));
|
|
14348
|
-
removedViaHash = true;
|
|
14349
|
-
}
|
|
14350
|
-
break;
|
|
14351
|
-
case "file_path":
|
|
14352
|
-
removed = removeFilePath(allowlist, value);
|
|
14353
|
-
break;
|
|
14354
|
-
}
|
|
14355
|
-
if (!removed) {
|
|
14356
|
-
return "Artifact not found in Sage allowlist.";
|
|
14357
|
-
}
|
|
14358
|
-
const saved = await saveAllowlist(allowlist, config.allowlist, logger);
|
|
14359
|
-
if (!saved) {
|
|
14360
|
-
return "Failed to save allowlist after removal. Please retry.";
|
|
14361
|
-
}
|
|
14362
|
-
let rendered;
|
|
14363
|
-
switch (type) {
|
|
14364
|
-
case "url":
|
|
14365
|
-
rendered = normalizeUrl(value);
|
|
14366
|
-
break;
|
|
14367
|
-
case "command":
|
|
14368
|
-
rendered = `${(removedViaHash ? hashCommand(value) : value).slice(0, 12)}...`;
|
|
14369
|
-
break;
|
|
14370
|
-
case "file_path":
|
|
14371
|
-
rendered = normalizeFilePath(value);
|
|
14372
|
-
break;
|
|
14373
|
-
}
|
|
14374
|
-
return `Removed ${type} from Sage allowlist: ${rendered}`;
|
|
14375
|
-
}
|
|
14376
14806
|
|
|
14377
14807
|
// ../core/dist/installation-id.js
|
|
14378
|
-
var
|
|
14808
|
+
var import_node_crypto6 = require("node:crypto");
|
|
14379
14809
|
var import_promises4 = require("node:fs/promises");
|
|
14380
|
-
var
|
|
14810
|
+
var import_node_path10 = require("node:path");
|
|
14381
14811
|
async function getInstallationId(sageDirPath) {
|
|
14382
14812
|
const sageDir = sageDirPath ?? resolvePath("~/.sage");
|
|
14383
|
-
const idPath = (0,
|
|
14813
|
+
const idPath = (0, import_node_path10.join)(sageDir, "installation-id");
|
|
14384
14814
|
let fileExists = false;
|
|
14385
14815
|
try {
|
|
14386
|
-
const existing = await (
|
|
14816
|
+
const existing = await getFileContent(idPath, "utf-8");
|
|
14387
14817
|
const trimmed = existing.trim();
|
|
14388
14818
|
if (trimmed.length > 0)
|
|
14389
14819
|
return trimmed;
|
|
@@ -14391,14 +14821,14 @@ async function getInstallationId(sageDirPath) {
|
|
|
14391
14821
|
} catch {
|
|
14392
14822
|
}
|
|
14393
14823
|
try {
|
|
14394
|
-
const id = (0,
|
|
14824
|
+
const id = (0, import_node_crypto6.randomUUID)();
|
|
14395
14825
|
await (0, import_promises4.mkdir)(sageDir, { recursive: true, mode: 448 });
|
|
14396
14826
|
await (0, import_promises4.writeFile)(idPath, id, { encoding: "utf-8", mode: 384, flag: fileExists ? "w" : "wx" });
|
|
14397
14827
|
return id;
|
|
14398
14828
|
} catch (err) {
|
|
14399
14829
|
if (err.code === "EEXIST") {
|
|
14400
14830
|
try {
|
|
14401
|
-
const existing = await (
|
|
14831
|
+
const existing = await getFileContent(idPath, "utf-8");
|
|
14402
14832
|
return existing.trim() || void 0;
|
|
14403
14833
|
} catch {
|
|
14404
14834
|
return void 0;
|
|
@@ -14409,17 +14839,16 @@ async function getInstallationId(sageDirPath) {
|
|
|
14409
14839
|
}
|
|
14410
14840
|
|
|
14411
14841
|
// ../core/dist/plugin-scan-cache.js
|
|
14412
|
-
var
|
|
14842
|
+
var import_node_crypto7 = require("node:crypto");
|
|
14413
14843
|
var import_promises5 = require("node:fs/promises");
|
|
14414
|
-
var
|
|
14415
|
-
var
|
|
14416
|
-
var DEFAULT_CACHE_PATH = (0, import_node_path8.join)((0, import_node_os3.homedir)(), ".sage", "plugin_scan_cache.json");
|
|
14844
|
+
var import_node_path11 = require("node:path");
|
|
14845
|
+
var DEFAULT_CACHE_PATH = (0, import_node_path11.join)(getHomeDir(), ".sage", "plugin_scan_cache.json");
|
|
14417
14846
|
var CACHE_TTL_DAYS = 7;
|
|
14418
14847
|
function cacheKey(pluginKey, version, lastUpdated) {
|
|
14419
14848
|
return `${pluginKey}:${version}:${lastUpdated}`;
|
|
14420
14849
|
}
|
|
14421
14850
|
async function computeConfigHash(sageVersion, ...dirs) {
|
|
14422
|
-
const h = (0,
|
|
14851
|
+
const h = (0, import_node_crypto7.createHash)("sha256");
|
|
14423
14852
|
if (sageVersion)
|
|
14424
14853
|
h.update(sageVersion);
|
|
14425
14854
|
for (const dir of dirs) {
|
|
@@ -14431,7 +14860,7 @@ async function computeConfigHash(sageVersion, ...dirs) {
|
|
|
14431
14860
|
}
|
|
14432
14861
|
for (const file of files) {
|
|
14433
14862
|
try {
|
|
14434
|
-
const content = await getFileContent((0,
|
|
14863
|
+
const content = await getFileContent((0, import_node_path11.join)(dir, file));
|
|
14435
14864
|
h.update(content);
|
|
14436
14865
|
} catch {
|
|
14437
14866
|
}
|
|
@@ -14521,15 +14950,16 @@ function storeResult(cache, pluginKey, version, lastUpdated, findings) {
|
|
|
14521
14950
|
}
|
|
14522
14951
|
|
|
14523
14952
|
// ../core/dist/plugin-scanner.js
|
|
14524
|
-
var
|
|
14953
|
+
var import_node_crypto8 = require("node:crypto");
|
|
14525
14954
|
var import_promises6 = require("node:fs/promises");
|
|
14526
|
-
var
|
|
14527
|
-
var
|
|
14528
|
-
var DEFAULT_PLUGINS_REGISTRY = (0, import_node_path9.join)((0, import_node_os4.homedir)(), ".claude", "plugins", "installed_plugins.json");
|
|
14955
|
+
var import_node_path12 = require("node:path");
|
|
14956
|
+
var DEFAULT_PLUGINS_REGISTRY = (0, import_node_path12.join)(getHomeDir(), ".claude", "plugins", "installed_plugins.json");
|
|
14529
14957
|
var SCANNABLE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
14530
14958
|
".py",
|
|
14531
14959
|
".js",
|
|
14532
14960
|
".ts",
|
|
14961
|
+
".mjs",
|
|
14962
|
+
".mts",
|
|
14533
14963
|
".sh",
|
|
14534
14964
|
".bash",
|
|
14535
14965
|
".zsh",
|
|
@@ -14555,7 +14985,7 @@ async function walkPluginFiles(installPath, logger) {
|
|
|
14555
14985
|
return;
|
|
14556
14986
|
}
|
|
14557
14987
|
if (stats.isFile()) {
|
|
14558
|
-
if (SCANNABLE_EXTENSIONS.has((0,
|
|
14988
|
+
if (SCANNABLE_EXTENSIONS.has((0, import_node_path12.extname)(dirOrFile).toLowerCase()) && stats.size <= MAX_FILE_SIZE) {
|
|
14559
14989
|
files.push(dirOrFile);
|
|
14560
14990
|
}
|
|
14561
14991
|
return;
|
|
@@ -14570,7 +15000,7 @@ async function walkPluginFiles(installPath, logger) {
|
|
|
14570
15000
|
for (const entry of entries) {
|
|
14571
15001
|
if (SKIP_DIRS.has(entry))
|
|
14572
15002
|
continue;
|
|
14573
|
-
const fullPath = (0,
|
|
15003
|
+
const fullPath = (0, import_node_path12.join)(dirOrFile, entry);
|
|
14574
15004
|
await walk(fullPath);
|
|
14575
15005
|
}
|
|
14576
15006
|
}
|
|
@@ -14588,6 +15018,82 @@ function isHarmlessEcho(line) {
|
|
|
14588
15018
|
const withoutQuotes = line.replace(/"(?:[^"\\]|\\.)*"|'[^']*'/g, "");
|
|
14589
15019
|
return !withoutQuotes.includes("|");
|
|
14590
15020
|
}
|
|
15021
|
+
var JS_TS_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".ts", ".mjs", ".mts"]);
|
|
15022
|
+
var STRING_OR_COMMENT_RE = /"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|`(?:[^`\\]|\\.)*`|\/\/.*$|\/\*[\s\S]*?\*\//gm;
|
|
15023
|
+
function stripJsComments(src) {
|
|
15024
|
+
return src.replace(STRING_OR_COMMENT_RE, (match) => {
|
|
15025
|
+
if (match.startsWith("//") || match.startsWith("/*"))
|
|
15026
|
+
return "";
|
|
15027
|
+
return match;
|
|
15028
|
+
});
|
|
15029
|
+
}
|
|
15030
|
+
var STR_ARG = `(?:"((?:[^"\\\\]|\\\\.)*)"|'((?:[^'\\\\]|\\\\.)*)'|\`([^\`]*)\`)`;
|
|
15031
|
+
var JS_EXEC_RE = new RegExp(`\\bexec(?:File)?(?:Sync)?\\s*\\(\\s*${STR_ARG}`, "g");
|
|
15032
|
+
var JS_SPAWN_RE = new RegExp(`\\bspawn(?:Sync)?\\s*\\(\\s*${STR_ARG}`, "g");
|
|
15033
|
+
var JS_SPAWN_ARGS_RE = /\bspawn(?:Sync)?\s*\(\s*(?:"((?:[^"\\]|\\.)*)"|'((?:[^'\\]|\\.)*)'|`([^`]*)`)\s*,\s*\[([^\]]*)\]/g;
|
|
15034
|
+
var SHELL_EXECUTABLES = /* @__PURE__ */ new Set([
|
|
15035
|
+
"bash",
|
|
15036
|
+
"sh",
|
|
15037
|
+
"zsh",
|
|
15038
|
+
"dash",
|
|
15039
|
+
"ksh",
|
|
15040
|
+
"csh",
|
|
15041
|
+
"fish",
|
|
15042
|
+
"cmd",
|
|
15043
|
+
"cmd.exe",
|
|
15044
|
+
"powershell",
|
|
15045
|
+
"powershell.exe",
|
|
15046
|
+
"pwsh"
|
|
15047
|
+
]);
|
|
15048
|
+
function isShellExecutable(exe) {
|
|
15049
|
+
const basename = exe.split("/").pop()?.split("\\").pop() ?? exe;
|
|
15050
|
+
return SHELL_EXECUTABLES.has(basename);
|
|
15051
|
+
}
|
|
15052
|
+
var JS_EXECA_RE = new RegExp(`\\bexeca\\s*\\(\\s*${STR_ARG}`, "g");
|
|
15053
|
+
var JS_EXECA_ARGS_RE = /\bexeca\s*\(\s*(?:"((?:[^"\\]|\\.)*)"|'((?:[^'\\]|\\.)*)'|`([^`]*)`)\s*,\s*\[([^\]]*)\]/g;
|
|
15054
|
+
var JS_BUN_SHELL_RE = new RegExp(`\\bBun\\.shell\\s*\\(\\s*${STR_ARG}`, "g");
|
|
15055
|
+
var JS_BUN_DOLLAR_RE = /\bBun\.\$`([^`]+)`/g;
|
|
15056
|
+
var JS_ZX_RE = /(?<!\w)\$`([^`]+)`/g;
|
|
15057
|
+
function extractCommandsFromJsTs(content, fileName) {
|
|
15058
|
+
const commands = /* @__PURE__ */ new Set();
|
|
15059
|
+
const stripped = stripJsComments(content);
|
|
15060
|
+
const collapsed = stripped.replace(/\n/g, " ");
|
|
15061
|
+
function addMatch(m) {
|
|
15062
|
+
const val = m[1] ?? m[2] ?? m[3];
|
|
15063
|
+
if (val)
|
|
15064
|
+
commands.add(val.trim());
|
|
15065
|
+
}
|
|
15066
|
+
for (const re of [JS_EXEC_RE, JS_SPAWN_RE, JS_EXECA_RE, JS_BUN_SHELL_RE]) {
|
|
15067
|
+
for (const m of collapsed.matchAll(re)) {
|
|
15068
|
+
addMatch(m);
|
|
15069
|
+
}
|
|
15070
|
+
}
|
|
15071
|
+
for (const re of [JS_BUN_DOLLAR_RE, JS_ZX_RE]) {
|
|
15072
|
+
for (const m of collapsed.matchAll(re)) {
|
|
15073
|
+
if (m[1])
|
|
15074
|
+
commands.add(m[1].trim());
|
|
15075
|
+
}
|
|
15076
|
+
}
|
|
15077
|
+
for (const re of [JS_SPAWN_ARGS_RE, JS_EXECA_ARGS_RE]) {
|
|
15078
|
+
for (const arrMatch of collapsed.matchAll(re)) {
|
|
15079
|
+
const exe = (arrMatch[1] ?? arrMatch[2] ?? arrMatch[3] ?? "").trim();
|
|
15080
|
+
if (!isShellExecutable(exe))
|
|
15081
|
+
continue;
|
|
15082
|
+
const arrayContent = arrMatch[4] ?? "";
|
|
15083
|
+
const strLitRe = new RegExp(STR_ARG, "g");
|
|
15084
|
+
for (const strMatch of arrayContent.matchAll(strLitRe)) {
|
|
15085
|
+
const val = strMatch[1] ?? strMatch[2] ?? strMatch[3];
|
|
15086
|
+
if (val)
|
|
15087
|
+
commands.add(val.trim());
|
|
15088
|
+
}
|
|
15089
|
+
}
|
|
15090
|
+
}
|
|
15091
|
+
return [...commands].map((value) => ({
|
|
15092
|
+
type: "command",
|
|
15093
|
+
value,
|
|
15094
|
+
context: `plugin_file:${fileName}`
|
|
15095
|
+
}));
|
|
15096
|
+
}
|
|
14591
15097
|
function extractArtifactsFromFile(filePath, content) {
|
|
14592
15098
|
const artifacts = [];
|
|
14593
15099
|
const fileName = filePath.split("/").pop() ?? filePath;
|
|
@@ -14596,7 +15102,7 @@ function extractArtifactsFromFile(filePath, content) {
|
|
|
14596
15102
|
continue;
|
|
14597
15103
|
artifacts.push({ type: "url", value: url, context: `plugin_file:${fileName}` });
|
|
14598
15104
|
}
|
|
14599
|
-
const ext = (0,
|
|
15105
|
+
const ext = (0, import_node_path12.extname)(filePath).toLowerCase();
|
|
14600
15106
|
if ([".sh", ".bash", ".zsh", ".py"].includes(ext)) {
|
|
14601
15107
|
for (const line of content.split("\n")) {
|
|
14602
15108
|
const trimmed = line.trim();
|
|
@@ -14609,6 +15115,9 @@ function extractArtifactsFromFile(filePath, content) {
|
|
|
14609
15115
|
}
|
|
14610
15116
|
}
|
|
14611
15117
|
}
|
|
15118
|
+
if (JS_TS_EXTENSIONS.has(ext)) {
|
|
15119
|
+
artifacts.push(...extractCommandsFromJsTs(content, fileName));
|
|
15120
|
+
}
|
|
14612
15121
|
return artifacts;
|
|
14613
15122
|
}
|
|
14614
15123
|
async function scanPlugin(plugin, threats, options = {}) {
|
|
@@ -14641,7 +15150,7 @@ async function scanPlugin(plugin, threats, options = {}) {
|
|
|
14641
15150
|
confidence: match.threat.confidence,
|
|
14642
15151
|
action: match.threat.action,
|
|
14643
15152
|
artifact: match.artifact.slice(0, 200),
|
|
14644
|
-
sourceFile: (0,
|
|
15153
|
+
sourceFile: (0, import_node_path12.relative)(plugin.installPath, filePath)
|
|
14645
15154
|
});
|
|
14646
15155
|
}
|
|
14647
15156
|
}
|
|
@@ -14649,7 +15158,7 @@ async function scanPlugin(plugin, threats, options = {}) {
|
|
|
14649
15158
|
allUrls.push(...extractUrls(content));
|
|
14650
15159
|
}
|
|
14651
15160
|
if (checkFileHashes) {
|
|
14652
|
-
const sha256 = (0,
|
|
15161
|
+
const sha256 = (0, import_node_crypto8.createHash)("sha256").update(rawBytes).digest("hex");
|
|
14653
15162
|
const existing = hashToFiles.get(sha256);
|
|
14654
15163
|
if (existing) {
|
|
14655
15164
|
existing.push(filePath);
|
|
@@ -14696,7 +15205,7 @@ async function scanPlugin(plugin, threats, options = {}) {
|
|
|
14696
15205
|
confidence: 1,
|
|
14697
15206
|
action: "block",
|
|
14698
15207
|
artifact: fr.sha256,
|
|
14699
|
-
sourceFile: (0,
|
|
15208
|
+
sourceFile: (0, import_node_path12.relative)(plugin.installPath, filePath)
|
|
14700
15209
|
});
|
|
14701
15210
|
}
|
|
14702
15211
|
}
|
|
@@ -14708,6 +15217,37 @@ async function scanPlugin(plugin, threats, options = {}) {
|
|
|
14708
15217
|
return result;
|
|
14709
15218
|
}
|
|
14710
15219
|
|
|
15220
|
+
// ../core/dist/sage-proxy.js
|
|
15221
|
+
function mapSageProxyOs(platform) {
|
|
15222
|
+
switch (platform) {
|
|
15223
|
+
case "win32":
|
|
15224
|
+
return "WINDOWS";
|
|
15225
|
+
case "darwin":
|
|
15226
|
+
return "MACOS";
|
|
15227
|
+
case "linux":
|
|
15228
|
+
return "LINUX";
|
|
15229
|
+
default:
|
|
15230
|
+
return platform;
|
|
15231
|
+
}
|
|
15232
|
+
}
|
|
15233
|
+
function mapSageProxyArchitecture(arch) {
|
|
15234
|
+
return arch.toUpperCase();
|
|
15235
|
+
}
|
|
15236
|
+
function buildSageProxyEnvelope(args) {
|
|
15237
|
+
return {
|
|
15238
|
+
identity: { uuid: args.iid },
|
|
15239
|
+
product: { version_app: args.versionApp },
|
|
15240
|
+
platform: {
|
|
15241
|
+
os: args.platformOs ?? mapSageProxyOs(process.platform),
|
|
15242
|
+
architecture: args.platformArchitecture ?? mapSageProxyArchitecture(process.arch)
|
|
15243
|
+
},
|
|
15244
|
+
agent: {
|
|
15245
|
+
agent_runtime: args.agentRuntime,
|
|
15246
|
+
agent_runtime_version: args.agentRuntimeVersion
|
|
15247
|
+
}
|
|
15248
|
+
};
|
|
15249
|
+
}
|
|
15250
|
+
|
|
14711
15251
|
// ../core/dist/session-start-scan.js
|
|
14712
15252
|
function fromCachedFinding(finding) {
|
|
14713
15253
|
return {
|
|
@@ -14746,11 +15286,39 @@ async function runSessionStartScan(context) {
|
|
|
14746
15286
|
if (plugins.length === 0) {
|
|
14747
15287
|
return [];
|
|
14748
15288
|
}
|
|
15289
|
+
let exceptions = [];
|
|
15290
|
+
try {
|
|
15291
|
+
const sageConfig = await loadConfig(context.configPath, logger);
|
|
15292
|
+
exceptions = await loadExceptions(sageConfig.exceptions, logger);
|
|
15293
|
+
} catch {
|
|
15294
|
+
}
|
|
14749
15295
|
const configHash = await computeConfigHash(context.sageVersion ?? "", context.threatsDir, context.allowlistsDir);
|
|
14750
15296
|
const cache = await loadScanCache(configHash, context.scanCachePath, logger);
|
|
14751
15297
|
const resultsWithFindings = [];
|
|
14752
15298
|
let cacheModified = false;
|
|
14753
15299
|
for (const plugin of plugins) {
|
|
15300
|
+
const denyMatch = findPluginDenyException(exceptions, plugin.key);
|
|
15301
|
+
if (denyMatch) {
|
|
15302
|
+
resultsWithFindings.push({
|
|
15303
|
+
plugin,
|
|
15304
|
+
findings: [
|
|
15305
|
+
{
|
|
15306
|
+
threatId: "EXCEPTION-DENY",
|
|
15307
|
+
title: `Denied by exception: ${denyMatch.pattern}${denyMatch.reason ? ` \u2014 ${denyMatch.reason}` : ""}`,
|
|
15308
|
+
severity: "critical",
|
|
15309
|
+
confidence: 1,
|
|
15310
|
+
action: "block",
|
|
15311
|
+
artifact: plugin.key,
|
|
15312
|
+
sourceFile: "~/.sage/exceptions.json"
|
|
15313
|
+
}
|
|
15314
|
+
]
|
|
15315
|
+
});
|
|
15316
|
+
continue;
|
|
15317
|
+
}
|
|
15318
|
+
const allowMatch = findPluginAllowException(exceptions, plugin.key);
|
|
15319
|
+
if (allowMatch) {
|
|
15320
|
+
continue;
|
|
15321
|
+
}
|
|
14754
15322
|
const cached = getCached(cache, plugin.key, plugin.version, plugin.lastUpdated);
|
|
14755
15323
|
if (cached && cached.findings.length === 0) {
|
|
14756
15324
|
continue;
|
|
@@ -14788,7 +15356,6 @@ async function runSessionStartScan(context) {
|
|
|
14788
15356
|
}
|
|
14789
15357
|
|
|
14790
15358
|
// ../core/dist/version-check.js
|
|
14791
|
-
var import_node_os5 = require("node:os");
|
|
14792
15359
|
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
14793
15360
|
function isNewerVersion(current, latest) {
|
|
14794
15361
|
const parse = (v) => v.replace(/^v/, "").split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
@@ -14810,22 +15377,23 @@ async function checkForUpdate(currentVersion, logger = nullLogger, timeoutMs = D
|
|
|
14810
15377
|
return null;
|
|
14811
15378
|
}
|
|
14812
15379
|
try {
|
|
14813
|
-
|
|
15380
|
+
if (!context.iid) {
|
|
15381
|
+
logger.debug("Skipping version check: missing installation id");
|
|
15382
|
+
return null;
|
|
15383
|
+
}
|
|
15384
|
+
const response = await fetch(resolveEndpoint("/v2/version-check"), {
|
|
14814
15385
|
method: "POST",
|
|
14815
15386
|
signal: AbortSignal.timeout(timeoutMs),
|
|
14816
15387
|
headers: {
|
|
14817
15388
|
Accept: "application/json",
|
|
14818
15389
|
"Content-Type": "application/json"
|
|
14819
15390
|
},
|
|
14820
|
-
body: JSON.stringify({
|
|
14821
|
-
|
|
14822
|
-
|
|
14823
|
-
|
|
14824
|
-
|
|
14825
|
-
|
|
14826
|
-
arch: process.arch,
|
|
14827
|
-
iid: context?.iid
|
|
14828
|
-
})
|
|
15391
|
+
body: JSON.stringify(buildSageProxyEnvelope({
|
|
15392
|
+
iid: context.iid,
|
|
15393
|
+
versionApp: currentVersion,
|
|
15394
|
+
agentRuntime: context.agentRuntime,
|
|
15395
|
+
agentRuntimeVersion: context.agentRuntimeVersion ?? "unknown"
|
|
15396
|
+
}))
|
|
14829
15397
|
});
|
|
14830
15398
|
if (!response.ok) {
|
|
14831
15399
|
logger.debug(`Version check HTTP ${response.status}`);
|
|
@@ -14867,11 +15435,16 @@ async function runSessionStart(ctx) {
|
|
|
14867
15435
|
checkUrls: ctx.checkUrls,
|
|
14868
15436
|
checkFileHashes: ctx.checkFileHashes
|
|
14869
15437
|
}),
|
|
14870
|
-
iidPromise.then((iid) =>
|
|
14871
|
-
|
|
14872
|
-
|
|
14873
|
-
|
|
14874
|
-
|
|
15438
|
+
iidPromise.then((iid) => {
|
|
15439
|
+
if (!iid) {
|
|
15440
|
+
return null;
|
|
15441
|
+
}
|
|
15442
|
+
return checkForUpdate(ctx.version, logger, void 0, {
|
|
15443
|
+
agentRuntime: ctx.agentRuntime,
|
|
15444
|
+
agentRuntimeVersion: ctx.agentRuntimeVersion,
|
|
15445
|
+
iid
|
|
15446
|
+
});
|
|
15447
|
+
}).catch(() => null)
|
|
14875
15448
|
]);
|
|
14876
15449
|
return { scanResults, versionCheck };
|
|
14877
15450
|
}
|
|
@@ -14910,79 +15483,20 @@ function createScanHandler(options) {
|
|
|
14910
15483
|
};
|
|
14911
15484
|
}
|
|
14912
15485
|
|
|
14913
|
-
// src/allowlist-tools.ts
|
|
14914
|
-
function createAllowlistAddTool(approvalStore, logger) {
|
|
14915
|
-
return {
|
|
14916
|
-
name: "sage_allowlist_add",
|
|
14917
|
-
description: "Permanently allow a URL, command, or file path after recent user approval through Sage.",
|
|
14918
|
-
parameters: {
|
|
14919
|
-
type: "object",
|
|
14920
|
-
properties: {
|
|
14921
|
-
type: {
|
|
14922
|
-
type: "string",
|
|
14923
|
-
enum: ["url", "command", "file_path"],
|
|
14924
|
-
description: "Artifact type"
|
|
14925
|
-
},
|
|
14926
|
-
value: {
|
|
14927
|
-
type: "string",
|
|
14928
|
-
description: "Exact URL, command, or file path to allowlist"
|
|
14929
|
-
},
|
|
14930
|
-
reason: {
|
|
14931
|
-
type: "string",
|
|
14932
|
-
description: "Optional reason for allowlist entry"
|
|
14933
|
-
}
|
|
14934
|
-
},
|
|
14935
|
-
required: ["type", "value"]
|
|
14936
|
-
},
|
|
14937
|
-
async execute(_toolCallId, params) {
|
|
14938
|
-
const { type, value, reason } = params;
|
|
14939
|
-
const msg = await addToAllowlist(approvalStore, type, value, reason, void 0, logger);
|
|
14940
|
-
return { content: [{ type: "text", text: msg }] };
|
|
14941
|
-
}
|
|
14942
|
-
};
|
|
14943
|
-
}
|
|
14944
|
-
function createAllowlistRemoveTool(logger) {
|
|
14945
|
-
return {
|
|
14946
|
-
name: "sage_allowlist_remove",
|
|
14947
|
-
description: "Remove a URL, command, or file path from the Sage allowlist.",
|
|
14948
|
-
parameters: {
|
|
14949
|
-
type: "object",
|
|
14950
|
-
properties: {
|
|
14951
|
-
type: {
|
|
14952
|
-
type: "string",
|
|
14953
|
-
enum: ["url", "command", "file_path"],
|
|
14954
|
-
description: "Artifact type"
|
|
14955
|
-
},
|
|
14956
|
-
value: {
|
|
14957
|
-
type: "string",
|
|
14958
|
-
description: "URL/file path, or command text/command hash for command entries"
|
|
14959
|
-
}
|
|
14960
|
-
},
|
|
14961
|
-
required: ["type", "value"]
|
|
14962
|
-
},
|
|
14963
|
-
async execute(_toolCallId, params) {
|
|
14964
|
-
const { type, value } = params;
|
|
14965
|
-
const msg = await removeFromAllowlist(type, value, void 0, logger);
|
|
14966
|
-
return { content: [{ type: "text", text: msg }] };
|
|
14967
|
-
}
|
|
14968
|
-
};
|
|
14969
|
-
}
|
|
14970
|
-
|
|
14971
15486
|
// src/bundled-dirs.ts
|
|
14972
|
-
var
|
|
14973
|
-
var
|
|
14974
|
-
var packageRoot = (0, import_node_path10.resolve)(__dirname, "..");
|
|
15487
|
+
var import_node_path13 = require("node:path");
|
|
15488
|
+
var packageRoot = (0, import_node_path13.resolve)(__dirname, "..");
|
|
14975
15489
|
function getBundledDataDirs() {
|
|
14976
15490
|
return {
|
|
14977
|
-
threatsDir: (0,
|
|
14978
|
-
allowlistsDir: (0,
|
|
15491
|
+
threatsDir: (0, import_node_path13.join)(packageRoot, "resources", "threats"),
|
|
15492
|
+
allowlistsDir: (0, import_node_path13.join)(packageRoot, "resources", "allowlists")
|
|
14979
15493
|
};
|
|
14980
15494
|
}
|
|
14981
15495
|
var cachedVersion;
|
|
14982
15496
|
function getSageVersion() {
|
|
14983
15497
|
if (cachedVersion) return cachedVersion;
|
|
14984
15498
|
try {
|
|
14985
|
-
const pkg = JSON.parse((
|
|
15499
|
+
const pkg = JSON.parse(getFileContentSync((0, import_node_path13.join)(packageRoot, "package.json"), "utf-8"));
|
|
14986
15500
|
cachedVersion = pkg.version ?? "0.0.0";
|
|
14987
15501
|
} catch {
|
|
14988
15502
|
cachedVersion = "0.0.0";
|
|
@@ -15047,9 +15561,9 @@ function createLogger(pluginLogger) {
|
|
|
15047
15561
|
|
|
15048
15562
|
// src/plugin-discovery.ts
|
|
15049
15563
|
var import_promises7 = require("node:fs/promises");
|
|
15050
|
-
var
|
|
15051
|
-
var
|
|
15052
|
-
var DEFAULT_EXTENSIONS_DIR = (0,
|
|
15564
|
+
var import_node_os2 = require("node:os");
|
|
15565
|
+
var import_node_path14 = require("node:path");
|
|
15566
|
+
var DEFAULT_EXTENSIONS_DIR = (0, import_node_path14.join)((0, import_node_os2.homedir)(), ".openclaw", "extensions");
|
|
15053
15567
|
async function discoverOpenClawPlugins(logger, extensionsDir = DEFAULT_EXTENSIONS_DIR) {
|
|
15054
15568
|
logger.info("Sage plugin discovery: scanning extensions directory", { path: extensionsDir });
|
|
15055
15569
|
let entries;
|
|
@@ -15061,7 +15575,7 @@ async function discoverOpenClawPlugins(logger, extensionsDir = DEFAULT_EXTENSION
|
|
|
15061
15575
|
}
|
|
15062
15576
|
const plugins = [];
|
|
15063
15577
|
for (const entry of entries) {
|
|
15064
|
-
const extDir = (0,
|
|
15578
|
+
const extDir = (0, import_node_path14.join)(extensionsDir, entry);
|
|
15065
15579
|
let stats;
|
|
15066
15580
|
try {
|
|
15067
15581
|
stats = await (0, import_promises7.stat)(extDir);
|
|
@@ -15071,7 +15585,7 @@ async function discoverOpenClawPlugins(logger, extensionsDir = DEFAULT_EXTENSION
|
|
|
15071
15585
|
}
|
|
15072
15586
|
let pkg;
|
|
15073
15587
|
try {
|
|
15074
|
-
const pkgPath = (0,
|
|
15588
|
+
const pkgPath = (0, import_node_path14.join)(extDir, "package.json");
|
|
15075
15589
|
const raw = await getFileContent(pkgPath);
|
|
15076
15590
|
pkg = JSON.parse(raw);
|
|
15077
15591
|
} catch {
|
|
@@ -15101,7 +15615,7 @@ function createOpenClawScanHandler(logger, context, onResult) {
|
|
|
15101
15615
|
logger,
|
|
15102
15616
|
context,
|
|
15103
15617
|
discoverPlugins: () => discoverOpenClawPlugins(logger),
|
|
15104
|
-
selfPrefix: "sage-openclaw@",
|
|
15618
|
+
selfPrefix: "@gendigital/sage-openclaw@",
|
|
15105
15619
|
threatsDir,
|
|
15106
15620
|
allowlistsDir,
|
|
15107
15621
|
version,
|
|
@@ -15175,7 +15689,14 @@ function createToolCallHandler(approvalStore, logger, threatsDir, allowlistsDir)
|
|
|
15175
15689
|
if (!artifacts || artifacts.length === 0) return void 0;
|
|
15176
15690
|
const sessionId = ctx?.sessionKey ?? "unknown";
|
|
15177
15691
|
const { verdict, actionId } = await guardToolCall(
|
|
15178
|
-
{
|
|
15692
|
+
{
|
|
15693
|
+
sessionId,
|
|
15694
|
+
conversationId: sessionId,
|
|
15695
|
+
agentRuntime: "openclaw",
|
|
15696
|
+
toolName,
|
|
15697
|
+
toolInput: params,
|
|
15698
|
+
artifacts
|
|
15699
|
+
},
|
|
15179
15700
|
{ threatsDir, allowlistsDir, logger },
|
|
15180
15701
|
approvalStore
|
|
15181
15702
|
);
|
|
@@ -15220,10 +15741,6 @@ var index_default = {
|
|
|
15220
15741
|
{ priority: 100 }
|
|
15221
15742
|
);
|
|
15222
15743
|
api.registerTool(createSageApproveTool(approvalStore));
|
|
15223
|
-
api.registerTool(
|
|
15224
|
-
createAllowlistAddTool(approvalStore, logger)
|
|
15225
|
-
);
|
|
15226
|
-
api.registerTool(createAllowlistRemoveTool(logger));
|
|
15227
15744
|
api.on("gateway_start", createStartupScanHandler(logger, onFindings));
|
|
15228
15745
|
api.on("session_start", createSessionScanHandler(logger, onFindings));
|
|
15229
15746
|
api.on(
|