@gendigital/sage-openclaw 0.11.0 → 0.13.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 +1 -1
- package/dist/index.cjs +1648 -489
- package/dist/index.cjs.map +4 -4
- package/dist/mcp-server.cjs +230 -96
- package/dist/mcp-server.cjs.map +4 -4
- package/dist/model-download-worker.cjs +139 -94
- package/dist/model-download-worker.cjs.map +3 -3
- package/dist/skill-upload-worker.cjs +5638 -0
- package/dist/skill-upload-worker.cjs.map +7 -0
- package/openclaw.plugin.json +6 -1
- package/package.json +8 -3
- package/resources/threats/_macros.yaml +193 -0
- package/resources/threats/agent-layer.yaml +19 -1
- package/resources/threats/commands.yaml +165 -31
- package/resources/threats/credentials.yaml +24 -4
- package/resources/threats/dummy.yaml +52 -17
- package/resources/threats/files.yaml +38 -0
- package/resources/threats/mac-commands.yaml +118 -27
- package/resources/threats/mac-credentials.yaml +34 -8
- package/resources/threats/mac-files.yaml +20 -0
- package/resources/threats/mac-obfuscation.yaml +28 -6
- package/resources/threats/mac-persistence.yaml +30 -4
- package/resources/threats/mac-supply-chain.yaml +12 -3
- package/resources/threats/mitre.yaml +325 -62
- package/resources/threats/obfuscation.yaml +33 -8
- package/resources/threats/persistence.yaml +40 -5
- package/resources/threats/prompt-injection.yaml +58 -0
- package/resources/threats/self-defense.yaml +15 -2
- package/resources/threats/supply_chain.yaml +6 -1
- package/resources/threats/urls.yaml +10 -0
- package/resources/threats/win-commands.yaml +294 -60
- package/resources/threats/win-credentials.yaml +68 -9
- package/resources/threats/win-files.yaml +20 -0
- package/resources/threats/win-obfuscation.yaml +56 -8
- package/resources/threats/win-persistence.yaml +45 -7
- package/resources/threats/win-supply-chain.yaml +5 -1
package/dist/mcp-server.cjs
CHANGED
|
@@ -46,13 +46,54 @@ function getProcEnv() {
|
|
|
46
46
|
function getHomeDir() {
|
|
47
47
|
return getProcEnv().HOME || (0, import_node_os.homedir)();
|
|
48
48
|
}
|
|
49
|
-
|
|
49
|
+
async function removeStaleFileLock(lockPath, staleAgeMs) {
|
|
50
|
+
try {
|
|
51
|
+
const s = await fsPromises.stat(lockPath);
|
|
52
|
+
if (Date.now() - s.mtimeMs < staleAgeMs)
|
|
53
|
+
return;
|
|
54
|
+
await fsPromises.rmdir(lockPath);
|
|
55
|
+
} catch {
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function acquireFileLock(filePath, timeoutMs = 250, staleAgeMs = 3e4) {
|
|
59
|
+
const lockPath = `${filePath}.lock`;
|
|
60
|
+
const deadline = Date.now() + timeoutMs;
|
|
61
|
+
while (true) {
|
|
62
|
+
try {
|
|
63
|
+
await fsPromises.mkdir(lockPath);
|
|
64
|
+
return async () => {
|
|
65
|
+
try {
|
|
66
|
+
await fsPromises.rmdir(lockPath);
|
|
67
|
+
} catch {
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
} catch {
|
|
71
|
+
await removeStaleFileLock(lockPath, staleAgeMs);
|
|
72
|
+
const remainingMs = deadline - Date.now();
|
|
73
|
+
if (remainingMs <= 0)
|
|
74
|
+
return void 0;
|
|
75
|
+
await (0, import_promises.setTimeout)(Math.min(50, remainingMs));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async function withFileLock(filePath, callback) {
|
|
80
|
+
const release = await acquireFileLock(filePath);
|
|
81
|
+
if (!release)
|
|
82
|
+
return;
|
|
83
|
+
try {
|
|
84
|
+
return await callback();
|
|
85
|
+
} finally {
|
|
86
|
+
await release();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
var fs, fsPromises, import_node_os, import_promises, name1, name2;
|
|
50
90
|
var init_file_utils = __esm({
|
|
51
91
|
"../core/dist/file-utils.js"() {
|
|
52
92
|
"use strict";
|
|
53
93
|
fs = __toESM(require("node:fs"), 1);
|
|
54
94
|
fsPromises = __toESM(require("node:fs/promises"), 1);
|
|
55
95
|
import_node_os = require("node:os");
|
|
96
|
+
import_promises = require("node:timers/promises");
|
|
56
97
|
name1 = "read";
|
|
57
98
|
name2 = "File";
|
|
58
99
|
}
|
|
@@ -4165,7 +4206,7 @@ var init_zod = __esm({
|
|
|
4165
4206
|
});
|
|
4166
4207
|
|
|
4167
4208
|
// ../core/dist/types.js
|
|
4168
|
-
var nullLogger, ArtifactTypeSchema, ArtifactSchema, VerdictSeveritySchema, ThreatSchema, DecisionSchema, SensitivitySchema, UrlCheckConfigSchema, CacheConfigSchema, LoggingConfigSchema, OperationalLogLevelSchema, OperationalLoggingConfigSchema, FileCheckConfigSchema, PackageCheckConfigSchema, AmsiCheckConfigSchema,
|
|
4209
|
+
var nullLogger, ArtifactTypeSchema, ArtifactSchema, VerdictSeveritySchema, ThreatSchema, DecisionSchema, SensitivitySchema, UrlCheckConfigSchema, CacheConfigSchema, LoggingConfigSchema, OperationalLogLevelSchema, OperationalLoggingConfigSchema, FileCheckConfigSchema, PackageCheckConfigSchema, AmsiCheckConfigSchema, SkillCheckConfigSchema, PiCheckConfigSchema, ExceptionDecisionSchema, ExceptionMatchSchema, ExceptionRuleSchema, ExceptionsFileSchema, ExceptionsConfigSchema, ConfigSchema, HookTypeSchema;
|
|
4169
4210
|
var init_types2 = __esm({
|
|
4170
4211
|
"../core/dist/types.js"() {
|
|
4171
4212
|
"use strict";
|
|
@@ -4189,7 +4230,8 @@ var init_types2 = __esm({
|
|
|
4189
4230
|
VerdictSeveritySchema = external_exports.enum(["info", "warning", "critical"]);
|
|
4190
4231
|
ThreatSchema = external_exports.object({
|
|
4191
4232
|
id: external_exports.string(),
|
|
4192
|
-
version: external_exports.number().int().
|
|
4233
|
+
version: external_exports.number().int().positive(),
|
|
4234
|
+
detectionName: external_exports.string(),
|
|
4193
4235
|
category: external_exports.string(),
|
|
4194
4236
|
severity: VerdictSeveritySchema,
|
|
4195
4237
|
confidence: external_exports.number(),
|
|
@@ -4242,14 +4284,21 @@ var init_types2 = __esm({
|
|
|
4242
4284
|
AmsiCheckConfigSchema = external_exports.object({
|
|
4243
4285
|
enabled: external_exports.boolean().default(true)
|
|
4244
4286
|
});
|
|
4245
|
-
|
|
4246
|
-
|
|
4287
|
+
SkillCheckConfigSchema = external_exports.object({
|
|
4288
|
+
enabled: external_exports.boolean().default(true),
|
|
4289
|
+
cache_ttl_days: external_exports.number().min(0).default(1),
|
|
4290
|
+
/**
|
|
4291
|
+
* Upload unknown skills (never seen by the analyzer) for deep content
|
|
4292
|
+
* analysis. When false, the scan still looks skills up by content hash and
|
|
4293
|
+
* still flags known-risky ones — but no skill content ever leaves the
|
|
4294
|
+
* machine and the upload worker never runs (lookup-only mode).
|
|
4295
|
+
*/
|
|
4296
|
+
upload_enabled: external_exports.boolean().default(true)
|
|
4297
|
+
});
|
|
4247
4298
|
PiCheckConfigSchema = external_exports.object({
|
|
4248
4299
|
enabled: external_exports.boolean().default(false),
|
|
4249
4300
|
max_content_length: external_exports.number().default(16384),
|
|
4250
|
-
model_path: external_exports.string().optional()
|
|
4251
|
-
high_risk_threshold: external_exports.number().default(DEFAULT_PI_HIGH_RISK_THRESHOLD),
|
|
4252
|
-
medium_risk_threshold: external_exports.number().default(DEFAULT_PI_MEDIUM_RISK_THRESHOLD)
|
|
4301
|
+
model_path: external_exports.string().optional()
|
|
4253
4302
|
});
|
|
4254
4303
|
ExceptionDecisionSchema = external_exports.enum(["allow", "deny"]);
|
|
4255
4304
|
ExceptionMatchSchema = external_exports.enum(["executable", "domain", "path", "plugin", "regex"]);
|
|
@@ -4271,6 +4320,7 @@ var init_types2 = __esm({
|
|
|
4271
4320
|
file_check: FileCheckConfigSchema.default({}),
|
|
4272
4321
|
package_check: PackageCheckConfigSchema.default({}),
|
|
4273
4322
|
amsi_check: AmsiCheckConfigSchema.default({}),
|
|
4323
|
+
skill_check: SkillCheckConfigSchema.default({}),
|
|
4274
4324
|
pi_check: PiCheckConfigSchema.default({}),
|
|
4275
4325
|
heuristics_enabled: external_exports.boolean().default(true),
|
|
4276
4326
|
cache: CacheConfigSchema.default({}),
|
|
@@ -4279,6 +4329,8 @@ var init_types2 = __esm({
|
|
|
4279
4329
|
operational_logging: OperationalLoggingConfigSchema.default({}),
|
|
4280
4330
|
sensitivity: SensitivitySchema.default("balanced"),
|
|
4281
4331
|
disabled_threats: external_exports.array(external_exports.string()).default([]),
|
|
4332
|
+
announce_clean_scans: external_exports.boolean().default(true),
|
|
4333
|
+
manage_status_line: external_exports.boolean().default(true),
|
|
4282
4334
|
brand_key: external_exports.string().min(1).max(32).regex(/^[a-z0-9_-]+$/u).optional(),
|
|
4283
4335
|
community_iq: external_exports.boolean().default(true)
|
|
4284
4336
|
});
|
|
@@ -4362,7 +4414,7 @@ function sanitizeBrandKey(data, logger2) {
|
|
|
4362
4414
|
if (typeof brandKey === "string" && brandKey.length >= 1 && brandKey.length <= 32 && BRAND_KEY_RE.test(brandKey)) {
|
|
4363
4415
|
return data;
|
|
4364
4416
|
}
|
|
4365
|
-
logger2.warn(`Invalid brand_key in config
|
|
4417
|
+
logger2.warn(`Invalid brand_key in config - ignoring`, { brand_key: brandKey });
|
|
4366
4418
|
const { brand_key: _, ...rest } = data;
|
|
4367
4419
|
return rest;
|
|
4368
4420
|
}
|
|
@@ -4414,6 +4466,21 @@ function parseConfig(raw, path2, logger2) {
|
|
|
4414
4466
|
return defaultConfig(logger2);
|
|
4415
4467
|
}
|
|
4416
4468
|
}
|
|
4469
|
+
async function readExplicitSkillUploadEnabled(configPath, logger2 = nullLogger) {
|
|
4470
|
+
const path2 = configPath ? resolvePath(configPath) : defaultConfigPath();
|
|
4471
|
+
try {
|
|
4472
|
+
const data = JSON.parse(await getFileContent(path2));
|
|
4473
|
+
const skillCheck = data.skill_check;
|
|
4474
|
+
if (skillCheck && typeof skillCheck === "object" && !Array.isArray(skillCheck) && "upload_enabled" in skillCheck) {
|
|
4475
|
+
const value = skillCheck.upload_enabled;
|
|
4476
|
+
if (typeof value === "boolean")
|
|
4477
|
+
return { present: true, value };
|
|
4478
|
+
logger2.warn("Config skill_check.upload_enabled is not a boolean; ignoring", { value });
|
|
4479
|
+
}
|
|
4480
|
+
} catch {
|
|
4481
|
+
}
|
|
4482
|
+
return { present: false, value: false };
|
|
4483
|
+
}
|
|
4417
4484
|
async function loadConfig(configPath, logger2 = nullLogger) {
|
|
4418
4485
|
const path2 = configPath ? resolvePath(configPath) : defaultConfigPath();
|
|
4419
4486
|
try {
|
|
@@ -4422,7 +4489,7 @@ async function loadConfig(configPath, logger2 = nullLogger) {
|
|
|
4422
4489
|
return defaultConfig(logger2);
|
|
4423
4490
|
}
|
|
4424
4491
|
}
|
|
4425
|
-
var import_node_path, SAGE_DIR, BRAND_KEY_RE;
|
|
4492
|
+
var import_node_path, SAGE_DIR, MS_PER_DAY, CLOCK_SKEW_TOLERANCE_MS, BRAND_KEY_RE;
|
|
4426
4493
|
var init_config = __esm({
|
|
4427
4494
|
"../core/dist/config.js"() {
|
|
4428
4495
|
"use strict";
|
|
@@ -4430,6 +4497,8 @@ var init_config = __esm({
|
|
|
4430
4497
|
init_file_utils();
|
|
4431
4498
|
init_types2();
|
|
4432
4499
|
SAGE_DIR = "~/.sage";
|
|
4500
|
+
MS_PER_DAY = 24 * 60 * 60 * 1e3;
|
|
4501
|
+
CLOCK_SKEW_TOLERANCE_MS = 5 * 60 * 1e3;
|
|
4433
4502
|
BRAND_KEY_RE = /^[a-z0-9_-]+$/u;
|
|
4434
4503
|
}
|
|
4435
4504
|
});
|
|
@@ -20440,7 +20509,7 @@ function resolveBranding(brandKey, logger2) {
|
|
|
20440
20509
|
return defaultBranding;
|
|
20441
20510
|
const entry = BRANDS[brandKey];
|
|
20442
20511
|
if (!entry) {
|
|
20443
|
-
logger2?.warn(`Unknown brand_key "${brandKey}" in config
|
|
20512
|
+
logger2?.warn(`Unknown brand_key "${brandKey}" in config - using default branding`);
|
|
20444
20513
|
return defaultBranding;
|
|
20445
20514
|
}
|
|
20446
20515
|
return { ...entry, brand_key: brandKey };
|
|
@@ -20456,88 +20525,74 @@ var APPROVED_TTL_MS = 10 * 60 * 1e3;
|
|
|
20456
20525
|
|
|
20457
20526
|
// ../core/dist/audit-log.js
|
|
20458
20527
|
init_config();
|
|
20528
|
+
|
|
20529
|
+
// ../core/dist/content-snapshot.js
|
|
20530
|
+
var CONTENT_FIELD_LIMITS = Object.freeze({
|
|
20531
|
+
command: 512,
|
|
20532
|
+
url: 512,
|
|
20533
|
+
file_path: 512,
|
|
20534
|
+
package_name: 256,
|
|
20535
|
+
package_version: 128,
|
|
20536
|
+
package_registry: 128
|
|
20537
|
+
});
|
|
20538
|
+
function safeTruncate(value, maxLen) {
|
|
20539
|
+
if (maxLen <= 0)
|
|
20540
|
+
return "";
|
|
20541
|
+
if (value.length <= maxLen)
|
|
20542
|
+
return value;
|
|
20543
|
+
const cutIndex = maxLen;
|
|
20544
|
+
const codeUnit = value.charCodeAt(cutIndex - 1);
|
|
20545
|
+
if (codeUnit >= 55296 && codeUnit <= 56319) {
|
|
20546
|
+
return value.slice(0, cutIndex - 1);
|
|
20547
|
+
}
|
|
20548
|
+
return value.slice(0, cutIndex);
|
|
20549
|
+
}
|
|
20550
|
+
|
|
20551
|
+
// ../core/dist/audit-log.js
|
|
20459
20552
|
init_file_utils();
|
|
20460
20553
|
|
|
20461
20554
|
// ../core/dist/jsonl-log-writer.js
|
|
20462
|
-
var
|
|
20555
|
+
var import_promises2 = require("node:fs/promises");
|
|
20463
20556
|
var import_node_path2 = require("node:path");
|
|
20464
|
-
var import_promises2 = require("node:timers/promises");
|
|
20465
20557
|
init_config();
|
|
20558
|
+
init_file_utils();
|
|
20466
20559
|
var writeQueues = /* @__PURE__ */ new Map();
|
|
20467
|
-
var ROTATE_LOCK_TIMEOUT_MS = 250;
|
|
20468
|
-
var ROTATE_LOCK_STALE_MS = 3e4;
|
|
20469
|
-
var ROTATE_LOCK_POLL_MS = 50;
|
|
20470
20560
|
async function shouldRotate(filePath, maxBytes, maxFiles) {
|
|
20471
20561
|
if (maxBytes <= 0 || maxFiles <= 0)
|
|
20472
20562
|
return false;
|
|
20473
20563
|
try {
|
|
20474
|
-
const s = await (0,
|
|
20564
|
+
const s = await (0, import_promises2.stat)(filePath);
|
|
20475
20565
|
return s.size >= maxBytes;
|
|
20476
20566
|
} catch {
|
|
20477
20567
|
return false;
|
|
20478
20568
|
}
|
|
20479
20569
|
}
|
|
20480
|
-
async function removeStaleRotateLock(lockPath) {
|
|
20481
|
-
try {
|
|
20482
|
-
const s = await (0, import_promises.stat)(lockPath);
|
|
20483
|
-
if (Date.now() - s.mtimeMs < ROTATE_LOCK_STALE_MS)
|
|
20484
|
-
return;
|
|
20485
|
-
await (0, import_promises.rmdir)(lockPath);
|
|
20486
|
-
} catch {
|
|
20487
|
-
}
|
|
20488
|
-
}
|
|
20489
|
-
async function acquireRotateLock(filePath) {
|
|
20490
|
-
const lockPath = `${filePath}.lock`;
|
|
20491
|
-
const deadline = Date.now() + ROTATE_LOCK_TIMEOUT_MS;
|
|
20492
|
-
while (true) {
|
|
20493
|
-
try {
|
|
20494
|
-
await (0, import_promises.mkdir)(lockPath);
|
|
20495
|
-
return async () => {
|
|
20496
|
-
try {
|
|
20497
|
-
await (0, import_promises.rmdir)(lockPath);
|
|
20498
|
-
} catch {
|
|
20499
|
-
}
|
|
20500
|
-
};
|
|
20501
|
-
} catch {
|
|
20502
|
-
await removeStaleRotateLock(lockPath);
|
|
20503
|
-
const remainingMs = deadline - Date.now();
|
|
20504
|
-
if (remainingMs <= 0)
|
|
20505
|
-
return void 0;
|
|
20506
|
-
await (0, import_promises2.setTimeout)(Math.min(ROTATE_LOCK_POLL_MS, remainingMs));
|
|
20507
|
-
}
|
|
20508
|
-
}
|
|
20509
|
-
}
|
|
20510
20570
|
async function rotateIfNeeded(filePath, maxBytes, maxFiles) {
|
|
20511
20571
|
if (!await shouldRotate(filePath, maxBytes, maxFiles))
|
|
20512
20572
|
return;
|
|
20513
|
-
|
|
20514
|
-
if (!releaseLock)
|
|
20515
|
-
return;
|
|
20516
|
-
try {
|
|
20573
|
+
await withFileLock(filePath, async () => {
|
|
20517
20574
|
if (!await shouldRotate(filePath, maxBytes, maxFiles))
|
|
20518
20575
|
return;
|
|
20519
20576
|
try {
|
|
20520
|
-
await (0,
|
|
20577
|
+
await (0, import_promises2.unlink)(`${filePath}.${maxFiles}`);
|
|
20521
20578
|
} catch {
|
|
20522
20579
|
}
|
|
20523
20580
|
for (let i = maxFiles - 1; i >= 1; i--) {
|
|
20524
20581
|
try {
|
|
20525
|
-
await (0,
|
|
20582
|
+
await (0, import_promises2.rename)(`${filePath}.${i}`, `${filePath}.${i + 1}`);
|
|
20526
20583
|
} catch {
|
|
20527
20584
|
}
|
|
20528
20585
|
}
|
|
20529
20586
|
try {
|
|
20530
|
-
await (0,
|
|
20587
|
+
await (0, import_promises2.rename)(filePath, `${filePath}.1`);
|
|
20531
20588
|
} catch {
|
|
20532
20589
|
}
|
|
20533
|
-
}
|
|
20534
|
-
await releaseLock();
|
|
20535
|
-
}
|
|
20590
|
+
});
|
|
20536
20591
|
}
|
|
20537
20592
|
async function appendJsonlEntryNow(path2, config2, entry) {
|
|
20538
|
-
await (0,
|
|
20593
|
+
await (0, import_promises2.mkdir)((0, import_node_path2.dirname)(path2), { recursive: true });
|
|
20539
20594
|
await rotateIfNeeded(path2, config2.max_bytes, config2.max_files);
|
|
20540
|
-
await (0,
|
|
20595
|
+
await (0, import_promises2.appendFile)(path2, `${JSON.stringify(entry)}
|
|
20541
20596
|
`);
|
|
20542
20597
|
}
|
|
20543
20598
|
async function appendJsonlEntry(config2, entry) {
|
|
@@ -20555,6 +20610,7 @@ async function appendJsonlEntry(config2, entry) {
|
|
|
20555
20610
|
}
|
|
20556
20611
|
|
|
20557
20612
|
// ../core/dist/audit-log.js
|
|
20613
|
+
init_types2();
|
|
20558
20614
|
async function getRecentEntries(config2, limit = 100) {
|
|
20559
20615
|
const path2 = resolvePath(config2.path);
|
|
20560
20616
|
try {
|
|
@@ -20734,7 +20790,7 @@ init_file_utils();
|
|
|
20734
20790
|
var import_meta = {};
|
|
20735
20791
|
function resolveVersion() {
|
|
20736
20792
|
if (true)
|
|
20737
|
-
return "0.
|
|
20793
|
+
return "0.13.0";
|
|
20738
20794
|
try {
|
|
20739
20795
|
const pkgPath = (0, import_node_path3.join)((0, import_node_path3.dirname)((0, import_node_url.fileURLToPath)(import_meta.url)), "..", "package.json");
|
|
20740
20796
|
const pkg = JSON.parse(getFileContentSync(pkgPath));
|
|
@@ -20753,8 +20809,11 @@ init_config();
|
|
|
20753
20809
|
init_types2();
|
|
20754
20810
|
var STALE_LOCK_MS = 60 * 60 * 1e3;
|
|
20755
20811
|
|
|
20812
|
+
// ../core/dist/clients/model-manifest.js
|
|
20813
|
+
init_config();
|
|
20814
|
+
|
|
20756
20815
|
// ../core/dist/sage-proxy.js
|
|
20757
|
-
function
|
|
20816
|
+
function mapSageHostOs(platform) {
|
|
20758
20817
|
switch (platform) {
|
|
20759
20818
|
case "win32":
|
|
20760
20819
|
return "WINDOWS";
|
|
@@ -20766,21 +20825,37 @@ function mapSageProxyOs(platform) {
|
|
|
20766
20825
|
return platform;
|
|
20767
20826
|
}
|
|
20768
20827
|
}
|
|
20769
|
-
function
|
|
20828
|
+
function mapSageHostArchitecture(arch) {
|
|
20770
20829
|
return arch.toUpperCase();
|
|
20771
20830
|
}
|
|
20831
|
+
function buildSageUserConfig(config2) {
|
|
20832
|
+
return {
|
|
20833
|
+
sensitivity: config2.sensitivity,
|
|
20834
|
+
url_check_enabled: config2.url_check.enabled,
|
|
20835
|
+
file_check_enabled: config2.file_check.enabled,
|
|
20836
|
+
package_check_enabled: config2.package_check.enabled,
|
|
20837
|
+
heuristics_enabled: config2.heuristics_enabled,
|
|
20838
|
+
pi_check_enabled: config2.pi_check.enabled,
|
|
20839
|
+
community_iq_enabled: config2.community_iq,
|
|
20840
|
+
// Optional-chained + defaulted: real callers pass a loadConfig result (skill_check
|
|
20841
|
+
// always present via the schema default), but this feeds fail-open telemetry, so a
|
|
20842
|
+
// partial config must never crash the envelope build.
|
|
20843
|
+
skill_check_upload_enabled: config2.skill_check?.upload_enabled ?? true
|
|
20844
|
+
};
|
|
20845
|
+
}
|
|
20772
20846
|
function buildSageProxyEnvelope(args) {
|
|
20773
20847
|
return {
|
|
20774
20848
|
identity: { uuid: args.iid },
|
|
20775
20849
|
product: { version_app: args.versionApp },
|
|
20776
20850
|
platform: {
|
|
20777
|
-
os: args.platformOs ??
|
|
20778
|
-
architecture: args.platformArchitecture ??
|
|
20851
|
+
os: args.platformOs ?? mapSageHostOs(process.platform),
|
|
20852
|
+
architecture: args.platformArchitecture ?? mapSageHostArchitecture(process.arch)
|
|
20779
20853
|
},
|
|
20780
20854
|
agent: {
|
|
20781
20855
|
agent_runtime: args.agentRuntime,
|
|
20782
20856
|
agent_runtime_version: args.agentRuntimeVersion
|
|
20783
|
-
}
|
|
20857
|
+
},
|
|
20858
|
+
...args.config ? { config: buildSageUserConfig(args.config) } : {}
|
|
20784
20859
|
};
|
|
20785
20860
|
}
|
|
20786
20861
|
|
|
@@ -20817,39 +20892,24 @@ init_types2();
|
|
|
20817
20892
|
// ../core/dist/index.js
|
|
20818
20893
|
init_pi_deps_installer();
|
|
20819
20894
|
|
|
20895
|
+
// ../core/dist/clients/skill-analyze.js
|
|
20896
|
+
init_types2();
|
|
20897
|
+
|
|
20820
20898
|
// ../core/dist/clients/skill-check.js
|
|
20821
20899
|
init_types2();
|
|
20822
20900
|
|
|
20823
20901
|
// ../core/dist/index.js
|
|
20824
20902
|
init_config();
|
|
20825
20903
|
|
|
20904
|
+
// ../core/dist/config-defaults.js
|
|
20905
|
+
init_file_utils();
|
|
20906
|
+
init_types2();
|
|
20907
|
+
|
|
20826
20908
|
// ../core/dist/config-diagnostics.js
|
|
20827
20909
|
init_config();
|
|
20828
20910
|
init_file_utils();
|
|
20829
20911
|
init_types2();
|
|
20830
20912
|
|
|
20831
|
-
// ../core/dist/content-snapshot.js
|
|
20832
|
-
var CONTENT_FIELD_LIMITS = Object.freeze({
|
|
20833
|
-
command: 512,
|
|
20834
|
-
url: 512,
|
|
20835
|
-
file_path: 512,
|
|
20836
|
-
package_name: 256,
|
|
20837
|
-
package_version: 128,
|
|
20838
|
-
package_registry: 128
|
|
20839
|
-
});
|
|
20840
|
-
function safeTruncate(value, maxLen) {
|
|
20841
|
-
if (maxLen <= 0)
|
|
20842
|
-
return "";
|
|
20843
|
-
if (value.length <= maxLen)
|
|
20844
|
-
return value;
|
|
20845
|
-
const cutIndex = maxLen;
|
|
20846
|
-
const codeUnit = value.charCodeAt(cutIndex - 1);
|
|
20847
|
-
if (codeUnit >= 55296 && codeUnit <= 56319) {
|
|
20848
|
-
return value.slice(0, cutIndex - 1);
|
|
20849
|
-
}
|
|
20850
|
-
return value.slice(0, cutIndex);
|
|
20851
|
-
}
|
|
20852
|
-
|
|
20853
20913
|
// ../core/dist/extended-info.js
|
|
20854
20914
|
var import_promises3 = require("node:fs/promises");
|
|
20855
20915
|
var import_node_os2 = require("node:os");
|
|
@@ -21056,6 +21116,9 @@ async function getInstallationId(sageDirPath) {
|
|
|
21056
21116
|
// ../core/dist/detection-telemetry.js
|
|
21057
21117
|
init_types2();
|
|
21058
21118
|
|
|
21119
|
+
// ../core/dist/e2e-capture.js
|
|
21120
|
+
var CAPTURE_MAX_BYTES = 10 * 1024 * 1024;
|
|
21121
|
+
|
|
21059
21122
|
// ../core/dist/policy.js
|
|
21060
21123
|
init_types2();
|
|
21061
21124
|
|
|
@@ -21102,6 +21165,32 @@ init_file_utils();
|
|
|
21102
21165
|
init_config();
|
|
21103
21166
|
init_types2();
|
|
21104
21167
|
|
|
21168
|
+
// ../core/dist/install-state.js
|
|
21169
|
+
init_config();
|
|
21170
|
+
init_file_utils();
|
|
21171
|
+
|
|
21172
|
+
// ../core/dist/notices.js
|
|
21173
|
+
init_config();
|
|
21174
|
+
var SKILL_UPLOAD_NOTICE = {
|
|
21175
|
+
id: "skill_upload_v1",
|
|
21176
|
+
body: () => [
|
|
21177
|
+
"unknown skills \u2014 each skill's SKILL.md and every supporting file in its",
|
|
21178
|
+
"folder \u2014 will be uploaded and checked against potential malicious artifacts",
|
|
21179
|
+
"starting next session. To keep skill content on this machine, set",
|
|
21180
|
+
'"skill_check": { "upload_enabled": false } in ~/.sage/config.json.'
|
|
21181
|
+
],
|
|
21182
|
+
// Suppressed once the user has explicitly set upload_enabled either way: an
|
|
21183
|
+
// explicit choice needs no consent prompt (the rollout obeys it directly).
|
|
21184
|
+
isEligible: async ({ configPath, logger: logger2 }) => !(await readExplicitSkillUploadEnabled(configPath, logger2)).present
|
|
21185
|
+
};
|
|
21186
|
+
var NOTICES = {
|
|
21187
|
+
[SKILL_UPLOAD_NOTICE.id]: SKILL_UPLOAD_NOTICE
|
|
21188
|
+
};
|
|
21189
|
+
|
|
21190
|
+
// ../core/dist/install-state.js
|
|
21191
|
+
init_types2();
|
|
21192
|
+
var SKILL_UPLOAD_NOTICE_ID = SKILL_UPLOAD_NOTICE.id;
|
|
21193
|
+
|
|
21105
21194
|
// ../core/dist/model-download.js
|
|
21106
21195
|
init_types2();
|
|
21107
21196
|
|
|
@@ -21223,6 +21312,7 @@ function createOperationalLogger(config2, runtime) {
|
|
|
21223
21312
|
|
|
21224
21313
|
// ../core/dist/plugin-scan-cache.js
|
|
21225
21314
|
var import_node_path6 = require("node:path");
|
|
21315
|
+
init_config();
|
|
21226
21316
|
init_file_utils();
|
|
21227
21317
|
init_types2();
|
|
21228
21318
|
var DEFAULT_CACHE_PATH = (0, import_node_path6.join)(getHomeDir(), ".sage", "plugin_scan_cache.json");
|
|
@@ -21230,6 +21320,22 @@ var DEFAULT_CACHE_PATH = (0, import_node_path6.join)(getHomeDir(), ".sage", "plu
|
|
|
21230
21320
|
// ../core/dist/plugin-scanner.js
|
|
21231
21321
|
init_config();
|
|
21232
21322
|
init_file_utils();
|
|
21323
|
+
|
|
21324
|
+
// ../core/dist/skill-id.js
|
|
21325
|
+
var MAX_SKILL_BYTES = 50 * 1024 * 1024;
|
|
21326
|
+
|
|
21327
|
+
// ../core/dist/skill-pending.js
|
|
21328
|
+
init_config();
|
|
21329
|
+
init_file_utils();
|
|
21330
|
+
init_types2();
|
|
21331
|
+
var PENDING_TTL_MS = 60 * 60 * 1e3;
|
|
21332
|
+
|
|
21333
|
+
// ../core/dist/skill-verdict-cache.js
|
|
21334
|
+
init_config();
|
|
21335
|
+
init_file_utils();
|
|
21336
|
+
init_types2();
|
|
21337
|
+
|
|
21338
|
+
// ../core/dist/plugin-scanner.js
|
|
21233
21339
|
init_types2();
|
|
21234
21340
|
var MAX_FILE_SIZE = 512 * 1024;
|
|
21235
21341
|
|
|
@@ -21265,6 +21371,7 @@ init_types2();
|
|
|
21265
21371
|
init_types2();
|
|
21266
21372
|
|
|
21267
21373
|
// ../core/dist/version-check.js
|
|
21374
|
+
init_config();
|
|
21268
21375
|
init_types2();
|
|
21269
21376
|
|
|
21270
21377
|
// ../core/dist/tool-names.js
|
|
@@ -31332,6 +31439,9 @@ function readContent(entry) {
|
|
|
31332
31439
|
return {};
|
|
31333
31440
|
return raw;
|
|
31334
31441
|
}
|
|
31442
|
+
function readContentSnippet(entry) {
|
|
31443
|
+
return asString(entry.content_snippet);
|
|
31444
|
+
}
|
|
31335
31445
|
function isRuntimeVerdictEntry(v) {
|
|
31336
31446
|
if (!v || typeof v !== "object" || Array.isArray(v))
|
|
31337
31447
|
return false;
|
|
@@ -31377,9 +31487,10 @@ var ReportInputSchema = external_exports.object({
|
|
|
31377
31487
|
entry_ids: external_exports.array(external_exports.string()).optional().describe(`REQUIRED in normal use: list of specific audit entry_ids to report as false positives. ALWAYS call sage_list_audit_entries first to inspect the audit log and identify the exact deny/ask verdict the user considers a false positive, then pass its entry_id here. Up to ${MAX_EXPLICIT_ENTRIES} entry_ids may be provided per call. Omitting this parameter is a fallback that submits up to ${MAX_IMPLICIT_ENTRIES} most recent deny/ask entries for the conversation, which may include irrelevant verdicts and should only be used when entry_id discovery is impossible.`),
|
|
31378
31488
|
dry_run: external_exports.boolean().optional().describe("If true, do not POST; just show the payload.")
|
|
31379
31489
|
});
|
|
31380
|
-
function parseAuditSignals(raw) {
|
|
31490
|
+
function parseAuditSignals(raw, options = {}) {
|
|
31381
31491
|
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
31382
31492
|
return {};
|
|
31493
|
+
const includePiContentSnippet = options.includePiContentSnippet ?? true;
|
|
31383
31494
|
const obj = raw;
|
|
31384
31495
|
const heuristicsRaw = obj.heuristics;
|
|
31385
31496
|
const urlChecksRaw = obj.url_checks;
|
|
@@ -31391,11 +31502,16 @@ function parseAuditSignals(raw) {
|
|
|
31391
31502
|
if (!h || typeof h !== "object" || Array.isArray(h))
|
|
31392
31503
|
return null;
|
|
31393
31504
|
const rec = h;
|
|
31505
|
+
const detection_name = asString(rec.detection_name);
|
|
31394
31506
|
const rule_id = asString(rec.rule_id);
|
|
31395
31507
|
const rule_version = typeof rec.rule_version === "number" ? Math.trunc(rec.rule_version) : void 0;
|
|
31396
31508
|
if (!rule_id)
|
|
31397
31509
|
return null;
|
|
31398
|
-
return {
|
|
31510
|
+
return {
|
|
31511
|
+
...detection_name ? { detection_name } : {},
|
|
31512
|
+
rule_id,
|
|
31513
|
+
rule_version
|
|
31514
|
+
};
|
|
31399
31515
|
}).filter(Boolean) : void 0;
|
|
31400
31516
|
const url_checks = Array.isArray(urlChecksRaw) ? urlChecksRaw.map((u) => {
|
|
31401
31517
|
if (!u || typeof u !== "object" || Array.isArray(u))
|
|
@@ -31433,12 +31549,20 @@ function parseAuditSignals(raw) {
|
|
|
31433
31549
|
if (!m || typeof m !== "object" || Array.isArray(m))
|
|
31434
31550
|
return null;
|
|
31435
31551
|
const rec = m;
|
|
31552
|
+
const detection_name = asString(rec.detection_name);
|
|
31436
31553
|
const risk = typeof rec.risk === "number" ? rec.risk : void 0;
|
|
31437
31554
|
const model_id = asString(rec.model_id);
|
|
31438
31555
|
const content_name = asString(rec.content_name);
|
|
31556
|
+
const content_snippet = asString(rec.content_snippet);
|
|
31439
31557
|
if (risk === void 0 || !model_id || !content_name)
|
|
31440
31558
|
return null;
|
|
31441
|
-
return {
|
|
31559
|
+
return {
|
|
31560
|
+
...detection_name ? { detection_name } : {},
|
|
31561
|
+
risk,
|
|
31562
|
+
model_id,
|
|
31563
|
+
content_name,
|
|
31564
|
+
...content_snippet && includePiContentSnippet ? { content_snippet } : {}
|
|
31565
|
+
};
|
|
31442
31566
|
}).filter(Boolean) : void 0;
|
|
31443
31567
|
const amsi_checks = Array.isArray(amsiChecksRaw) ? amsiChecksRaw.map((a) => {
|
|
31444
31568
|
if (!a || typeof a !== "object" || Array.isArray(a))
|
|
@@ -31510,7 +31634,11 @@ function registerFalsePositiveTools(server, opts) {
|
|
|
31510
31634
|
severity: asString(e.severity),
|
|
31511
31635
|
source: asString(e.source),
|
|
31512
31636
|
user_override: e.user_override,
|
|
31513
|
-
|
|
31637
|
+
// PI snippets are untrusted fetched content. Exclude their raw
|
|
31638
|
+
// text—including the generic top-level content_snippet - from this
|
|
31639
|
+
// model-visible response. Retain it only for the backend-only
|
|
31640
|
+
// false-positive report below.
|
|
31641
|
+
signals: parseAuditSignals(e.signals, { includePiContentSnippet: false }),
|
|
31514
31642
|
content: readContent(e)
|
|
31515
31643
|
}));
|
|
31516
31644
|
complete("completed", {
|
|
@@ -31627,10 +31755,14 @@ ${reasoning}`;
|
|
|
31627
31755
|
const agent_runtime = asString(e.agent_runtime) ?? "unknown";
|
|
31628
31756
|
const tool_type = asString(e.tool_name) ?? "Unknown";
|
|
31629
31757
|
const verdict = asString(e.verdict) ?? "deny";
|
|
31630
|
-
const
|
|
31758
|
+
const wasAllowed = verdict === "allow" || e.user_override === true;
|
|
31759
|
+
const user_action = wasAllowed ? "allowed" : "blocked";
|
|
31631
31760
|
const hook_type = asHookType(e.hook_type) ?? "PreToolUse";
|
|
31632
|
-
const signals = parseAuditSignals(e.signals
|
|
31761
|
+
const signals = parseAuditSignals(e.signals, {
|
|
31762
|
+
includePiContentSnippet: !dry_run
|
|
31763
|
+
});
|
|
31633
31764
|
const content = readContent(e);
|
|
31765
|
+
const contentSnippet = readContentSnippet(e);
|
|
31634
31766
|
const bestEffortSignals = {};
|
|
31635
31767
|
if (signals.heuristics)
|
|
31636
31768
|
bestEffortSignals.heuristics = signals.heuristics;
|
|
@@ -31649,7 +31781,8 @@ ${reasoning}`;
|
|
|
31649
31781
|
iid,
|
|
31650
31782
|
versionApp,
|
|
31651
31783
|
agentRuntime: agent_runtime,
|
|
31652
|
-
agentRuntimeVersion: runtimeVersion
|
|
31784
|
+
agentRuntimeVersion: runtimeVersion,
|
|
31785
|
+
config: config2
|
|
31653
31786
|
}),
|
|
31654
31787
|
block_event: {
|
|
31655
31788
|
hook_type,
|
|
@@ -31658,7 +31791,8 @@ ${reasoning}`;
|
|
|
31658
31791
|
user_action,
|
|
31659
31792
|
timestamp,
|
|
31660
31793
|
...Object.keys(bestEffortSignals).length > 0 ? { signals: bestEffortSignals } : {},
|
|
31661
|
-
content
|
|
31794
|
+
content,
|
|
31795
|
+
...contentSnippet && !dry_run ? { content_snippet: contentSnippet } : {}
|
|
31662
31796
|
},
|
|
31663
31797
|
comment,
|
|
31664
31798
|
event_id: entry_id ?? (0, import_node_crypto2.randomUUID)()
|
|
@@ -31763,7 +31897,7 @@ async function main() {
|
|
|
31763
31897
|
);
|
|
31764
31898
|
const branding = resolveBranding(config2.brand_key, logger);
|
|
31765
31899
|
await runSageMcpServerStdio({
|
|
31766
|
-
version: "0.
|
|
31900
|
+
version: "0.13.0",
|
|
31767
31901
|
logger,
|
|
31768
31902
|
branding
|
|
31769
31903
|
});
|