palizade 0.1.1 → 0.2.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/dist/index.cjs +566 -41
- package/dist/templates.d.ts +2 -2
- package/dist/templates.d.ts.map +1 -1
- package/dist/templates.js +24 -1
- package/dist/templates.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -25387,6 +25387,7 @@ var capabilitySchema = external_exports.enum([
|
|
|
25387
25387
|
"reads_untrusted_content",
|
|
25388
25388
|
"reads_sensitive_data",
|
|
25389
25389
|
"network_egress",
|
|
25390
|
+
"file_write",
|
|
25390
25391
|
"writes_local",
|
|
25391
25392
|
"writes_remote",
|
|
25392
25393
|
"deletes_data",
|
|
@@ -25404,9 +25405,48 @@ var serverConfigSchema = external_exports.object({
|
|
|
25404
25405
|
trust: trustSchema.default("untrusted"),
|
|
25405
25406
|
toolClasses: external_exports.record(external_exports.string(), toolClassSchema).default({}),
|
|
25406
25407
|
toolCapabilities: external_exports.record(external_exports.string(), external_exports.array(capabilitySchema)).default({}),
|
|
25408
|
+
sensitive: external_exports.boolean().default(false),
|
|
25409
|
+
sensitiveTools: external_exports.record(external_exports.string(), external_exports.boolean()).default({}),
|
|
25410
|
+
sensitivePathPatterns: external_exports.array(external_exports.string()).default([]),
|
|
25407
25411
|
shell: external_exports.boolean().default(false),
|
|
25408
25412
|
allowShell: external_exports.boolean().default(false)
|
|
25409
25413
|
}).strict();
|
|
25414
|
+
var secretDetectorConfigSchema = external_exports.object({
|
|
25415
|
+
enabled: external_exports.boolean().default(false),
|
|
25416
|
+
aws: external_exports.boolean().default(true),
|
|
25417
|
+
generic: external_exports.boolean().default(true),
|
|
25418
|
+
jwt: external_exports.boolean().default(true),
|
|
25419
|
+
privateKey: external_exports.boolean().default(true),
|
|
25420
|
+
googleApiKey: external_exports.boolean().default(true),
|
|
25421
|
+
stripe: external_exports.boolean().default(true),
|
|
25422
|
+
slack: external_exports.boolean().default(true),
|
|
25423
|
+
github: external_exports.boolean().default(true),
|
|
25424
|
+
openai: external_exports.boolean().default(true)
|
|
25425
|
+
}).default({
|
|
25426
|
+
enabled: false,
|
|
25427
|
+
aws: true,
|
|
25428
|
+
generic: true,
|
|
25429
|
+
jwt: true,
|
|
25430
|
+
privateKey: true,
|
|
25431
|
+
googleApiKey: true,
|
|
25432
|
+
stripe: true,
|
|
25433
|
+
slack: true,
|
|
25434
|
+
github: true,
|
|
25435
|
+
openai: true
|
|
25436
|
+
});
|
|
25437
|
+
var piiDetectorConfigSchema = external_exports.object({
|
|
25438
|
+
enabled: external_exports.boolean().default(false),
|
|
25439
|
+
email: external_exports.boolean().default(true),
|
|
25440
|
+
ssn: external_exports.boolean().default(true),
|
|
25441
|
+
creditCard: external_exports.boolean().default(true),
|
|
25442
|
+
phone: external_exports.boolean().default(true)
|
|
25443
|
+
}).default({
|
|
25444
|
+
enabled: false,
|
|
25445
|
+
email: true,
|
|
25446
|
+
ssn: true,
|
|
25447
|
+
creditCard: true,
|
|
25448
|
+
phone: true
|
|
25449
|
+
});
|
|
25410
25450
|
var palizadeConfigSchema = external_exports.object({
|
|
25411
25451
|
stateDir: external_exports.string().default(".palizade"),
|
|
25412
25452
|
policy: external_exports.string().default("policies/default.yaml"),
|
|
@@ -25429,10 +25469,22 @@ var palizadeConfigSchema = external_exports.object({
|
|
|
25429
25469
|
model: external_exports.string().default("sinatras/Llama-Prompt-Guard-2-86M-ONNX"),
|
|
25430
25470
|
cacheDir: external_exports.string().optional(),
|
|
25431
25471
|
device: external_exports.string().default("cpu")
|
|
25432
|
-
}).default({ enabled: false, model: "sinatras/Llama-Prompt-Guard-2-86M-ONNX", device: "cpu" })
|
|
25472
|
+
}).default({ enabled: false, model: "sinatras/Llama-Prompt-Guard-2-86M-ONNX", device: "cpu" }),
|
|
25473
|
+
secrets: secretDetectorConfigSchema,
|
|
25474
|
+
pii: piiDetectorConfigSchema
|
|
25433
25475
|
}).default({
|
|
25434
25476
|
heuristic: true,
|
|
25435
|
-
promptGuard2: { enabled: false, model: "sinatras/Llama-Prompt-Guard-2-86M-ONNX", device: "cpu" }
|
|
25477
|
+
promptGuard2: { enabled: false, model: "sinatras/Llama-Prompt-Guard-2-86M-ONNX", device: "cpu" },
|
|
25478
|
+
secrets: { enabled: false, aws: true, generic: true, jwt: true, privateKey: true, googleApiKey: true, stripe: true, slack: true, github: true, openai: true },
|
|
25479
|
+
pii: { enabled: false, email: true, ssn: true, creditCard: true, phone: true }
|
|
25480
|
+
}),
|
|
25481
|
+
egress: external_exports.object({
|
|
25482
|
+
allowlist: external_exports.object({
|
|
25483
|
+
hosts: external_exports.array(external_exports.string()).default([]),
|
|
25484
|
+
emails: external_exports.array(external_exports.string()).default([])
|
|
25485
|
+
}).default({ hosts: [], emails: [] })
|
|
25486
|
+
}).default({
|
|
25487
|
+
allowlist: { hosts: [], emails: [] }
|
|
25436
25488
|
}),
|
|
25437
25489
|
transport: external_exports.object({
|
|
25438
25490
|
maxMessageBytes: external_exports.number().int().min(1024).default(64 * 1024 * 1024),
|
|
@@ -25520,7 +25572,7 @@ var SOURCE_RE = /\b(read|get|fetch|search|browse|list|download|crawl|open|load|q
|
|
|
25520
25572
|
var CAPABILITY_RULES = [
|
|
25521
25573
|
[/\b(fetch|http|post|put|patch|request|webhook|url|browser|crawl)\b/iu, ["network_egress", "reads_untrusted_content"]],
|
|
25522
25574
|
[/\b(email|mail|send|sms|slack|discord|message|publish)\b/iu, ["sends_message", "writes_remote", "network_egress"]],
|
|
25523
|
-
[/\b(write|save|edit|create|move|append)\b/iu, ["writes_local"]],
|
|
25575
|
+
[/\b(write|save|edit|create|move|append)\b/iu, ["file_write", "writes_local"]],
|
|
25524
25576
|
[/\b(delete|remove|rm|destroy)\b/iu, ["deletes_data"]],
|
|
25525
25577
|
[/\b(exec|shell|run|spawn|command|script|terminal)\b/iu, ["executes_code"]],
|
|
25526
25578
|
[/\b(secret|credential|token|key|env|password)\b/iu, ["accesses_credentials", "reads_sensitive_data"]],
|
|
@@ -25534,7 +25586,7 @@ function classifyToolDetailed(toolName, server, tool) {
|
|
|
25534
25586
|
for (const capability of capabilitiesFromAnnotations(tool?.annotations)) {
|
|
25535
25587
|
capabilities.add(capability);
|
|
25536
25588
|
}
|
|
25537
|
-
const searchable = `${toolName} ${tool?.title ?? ""} ${tool?.description ?? ""}
|
|
25589
|
+
const searchable = `${toolName} ${tool?.title ?? ""} ${tool?.description ?? ""}`.replace(/[_-]+/gu, " ");
|
|
25538
25590
|
for (const [regex, matched] of CAPABILITY_RULES) {
|
|
25539
25591
|
if (regex.test(searchable)) {
|
|
25540
25592
|
matched.forEach((capability) => capabilities.add(capability));
|
|
@@ -25564,6 +25616,7 @@ function capabilitiesFromAnnotations(annotations) {
|
|
|
25564
25616
|
const capabilities = /* @__PURE__ */ new Set();
|
|
25565
25617
|
if (annotations.destructiveHint === true) {
|
|
25566
25618
|
capabilities.add("writes_remote");
|
|
25619
|
+
capabilities.add("file_write");
|
|
25567
25620
|
capabilities.add("writes_local");
|
|
25568
25621
|
}
|
|
25569
25622
|
if (annotations.openWorldHint === true) {
|
|
@@ -25578,6 +25631,7 @@ function capabilitiesFromAnnotations(annotations) {
|
|
|
25578
25631
|
function deriveClass(toolName, capabilities) {
|
|
25579
25632
|
if ([...capabilities].some((capability) => [
|
|
25580
25633
|
"network_egress",
|
|
25634
|
+
"file_write",
|
|
25581
25635
|
"writes_local",
|
|
25582
25636
|
"writes_remote",
|
|
25583
25637
|
"deletes_data",
|
|
@@ -26480,8 +26534,176 @@ function promptGuardMaliciousScore(rows) {
|
|
|
26480
26534
|
return rows[0]?.score ?? 0;
|
|
26481
26535
|
}
|
|
26482
26536
|
|
|
26537
|
+
// ../detectors/dist/sensitive.js
|
|
26538
|
+
var SECRET_RULES = [
|
|
26539
|
+
{ label: "secret:aws-access-key-id", family: "aws", kind: "secret", score: 0.9, regex: /\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/gu },
|
|
26540
|
+
{ label: "secret:aws-secret-key", family: "aws", kind: "secret", score: 0.95, regex: /\baws[_-]?secret[_-]?access[_-]?key\s*[:=]\s*["']?([A-Za-z0-9/+=]{40})["']?/giu },
|
|
26541
|
+
{ label: "secret:openai", family: "openai", kind: "secret", score: 0.9, regex: /\bsk-[A-Za-z0-9]{20,}\b/gu },
|
|
26542
|
+
{ label: "secret:github", family: "github", kind: "secret", score: 0.9, regex: /\bgh[pousr]_[A-Za-z0-9_]{30,}\b/gu },
|
|
26543
|
+
{ label: "secret:slack", family: "slack", kind: "secret", score: 0.9, regex: /\bxox[abprs]-[A-Za-z0-9-]{20,}\b/gu },
|
|
26544
|
+
{ label: "secret:jwt", family: "jwt", kind: "secret", score: 0.85, regex: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/gu },
|
|
26545
|
+
{ label: "secret:jwt", family: "jwt", kind: "secret", score: 0.85, regex: /\bBearer\s+(eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,})\b/giu },
|
|
26546
|
+
{ label: "secret:private-key", family: "privateKey", kind: "secret", score: 1, regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]{24,}?-----END [A-Z ]*PRIVATE KEY-----/gu },
|
|
26547
|
+
{ label: "secret:google-api-key", family: "googleApiKey", kind: "secret", score: 0.9, regex: /\bAIza[0-9A-Za-z_-]{35}\b/gu },
|
|
26548
|
+
{ label: "secret:stripe", family: "stripe", kind: "secret", score: 0.9, regex: /\b[sp]k_live_[0-9A-Za-z]{16,}\b/gu },
|
|
26549
|
+
{
|
|
26550
|
+
label: "secret:assignment",
|
|
26551
|
+
family: "generic",
|
|
26552
|
+
kind: "secret",
|
|
26553
|
+
score: 0.8,
|
|
26554
|
+
regex: /\b(?:password|passwd|api[_-]?key|secret|token|access[_-]?token|client[_-]?secret)\s*[:=]\s*["']?([A-Za-z0-9_./+=-]{12,})["']?/giu,
|
|
26555
|
+
validate: (match) => shannonEntropy(match[1] ?? "") >= 3.2
|
|
26556
|
+
}
|
|
26557
|
+
];
|
|
26558
|
+
var PII_RULES = [
|
|
26559
|
+
{ label: "pii:email", family: "email", kind: "pii", score: 0.55, regex: /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/giu },
|
|
26560
|
+
{ label: "pii:ssn", family: "ssn", kind: "pii", score: 0.75, regex: /\b(?!000|666|9\d\d)\d{3}-(?!00)\d{2}-(?!0000)\d{4}\b/gu },
|
|
26561
|
+
{
|
|
26562
|
+
label: "pii:credit-card",
|
|
26563
|
+
family: "creditCard",
|
|
26564
|
+
kind: "pii",
|
|
26565
|
+
score: 0.75,
|
|
26566
|
+
regex: /\b(?:\d[ -]*?){13,19}\b/gu,
|
|
26567
|
+
validate: (match) => luhn(match[0].replace(/\D/gu, ""))
|
|
26568
|
+
},
|
|
26569
|
+
{ label: "pii:phone", family: "phone", kind: "pii", score: 0.45, regex: /\b(?:\+?1[\s.-]?)?(?:\(\d{3}\)|\d{3})[\s.-]\d{3}[\s.-]\d{4}\b/gu }
|
|
26570
|
+
];
|
|
26571
|
+
var SensitiveDataDetector = class {
|
|
26572
|
+
name = "sensitive";
|
|
26573
|
+
options;
|
|
26574
|
+
constructor(options = {}) {
|
|
26575
|
+
this.options = {
|
|
26576
|
+
secrets: { enabled: false, ...options.secrets },
|
|
26577
|
+
pii: { enabled: false, ...options.pii }
|
|
26578
|
+
};
|
|
26579
|
+
}
|
|
26580
|
+
detect(text, _ctx = {}) {
|
|
26581
|
+
if (!text.trim()) {
|
|
26582
|
+
return { score: 0, labels: [], spans: [], detector: this.name };
|
|
26583
|
+
}
|
|
26584
|
+
const spans = [];
|
|
26585
|
+
const labels = /* @__PURE__ */ new Set();
|
|
26586
|
+
let score = 0;
|
|
26587
|
+
if (this.options.secrets.enabled) {
|
|
26588
|
+
score = Math.max(score, this.applyRules(text, SECRET_RULES, this.options.secrets, spans, labels));
|
|
26589
|
+
}
|
|
26590
|
+
if (this.options.pii.enabled) {
|
|
26591
|
+
score = Math.max(score, this.applyRules(text, PII_RULES, this.options.pii, spans, labels));
|
|
26592
|
+
}
|
|
26593
|
+
return {
|
|
26594
|
+
score: clampScore(score),
|
|
26595
|
+
labels: [...labels],
|
|
26596
|
+
spans: mergeSensitiveSpans(spans),
|
|
26597
|
+
detector: this.name
|
|
26598
|
+
};
|
|
26599
|
+
}
|
|
26600
|
+
applyRules(text, rules, options, spans, labels) {
|
|
26601
|
+
let maxScore = 0;
|
|
26602
|
+
for (const rule of rules) {
|
|
26603
|
+
if (options[rule.family] === false) {
|
|
26604
|
+
continue;
|
|
26605
|
+
}
|
|
26606
|
+
for (const match of text.matchAll(rule.regex)) {
|
|
26607
|
+
if (rule.validate && !rule.validate(match)) {
|
|
26608
|
+
continue;
|
|
26609
|
+
}
|
|
26610
|
+
const start = match.index ?? 0;
|
|
26611
|
+
const end = start + match[0].length;
|
|
26612
|
+
labels.add(rule.label);
|
|
26613
|
+
spans.push({ start, end, label: rule.label });
|
|
26614
|
+
maxScore = Math.max(maxScore, rule.score);
|
|
26615
|
+
}
|
|
26616
|
+
}
|
|
26617
|
+
return maxScore;
|
|
26618
|
+
}
|
|
26619
|
+
};
|
|
26620
|
+
function isSecretLabel(label) {
|
|
26621
|
+
return label.startsWith("secret:");
|
|
26622
|
+
}
|
|
26623
|
+
function isPiiLabel(label) {
|
|
26624
|
+
return label.startsWith("pii:");
|
|
26625
|
+
}
|
|
26626
|
+
function hasSecretLabel(labels) {
|
|
26627
|
+
return labels.some(isSecretLabel);
|
|
26628
|
+
}
|
|
26629
|
+
function hasPiiLabel(labels) {
|
|
26630
|
+
return labels.some(isPiiLabel);
|
|
26631
|
+
}
|
|
26632
|
+
function maskSensitiveText(text, spans = []) {
|
|
26633
|
+
if (spans.length === 0) {
|
|
26634
|
+
return text;
|
|
26635
|
+
}
|
|
26636
|
+
let output2 = text;
|
|
26637
|
+
const sorted = [...spans].sort((left, right) => right.start - left.start || right.end - left.end);
|
|
26638
|
+
for (const span of sorted) {
|
|
26639
|
+
output2 = `${output2.slice(0, span.start)}[REDACTED:${span.label ?? "sensitive"}]${output2.slice(span.end)}`;
|
|
26640
|
+
}
|
|
26641
|
+
return output2;
|
|
26642
|
+
}
|
|
26643
|
+
function maskKnownSensitiveText(text) {
|
|
26644
|
+
const detector = new SensitiveDataDetector({
|
|
26645
|
+
secrets: { enabled: true },
|
|
26646
|
+
pii: { enabled: true }
|
|
26647
|
+
});
|
|
26648
|
+
const result = detector.detect(text);
|
|
26649
|
+
return maskSensitiveText(text, result.spans);
|
|
26650
|
+
}
|
|
26651
|
+
function mergeSensitiveSpans(spans) {
|
|
26652
|
+
const sorted = [...spans].sort((a, b) => a.start - b.start || a.end - b.end);
|
|
26653
|
+
const merged = [];
|
|
26654
|
+
for (const span of sorted) {
|
|
26655
|
+
const last = merged[merged.length - 1];
|
|
26656
|
+
if (!last || span.start > last.end) {
|
|
26657
|
+
merged.push({ ...span });
|
|
26658
|
+
continue;
|
|
26659
|
+
}
|
|
26660
|
+
last.end = Math.max(last.end, span.end);
|
|
26661
|
+
if (last.label !== span.label) {
|
|
26662
|
+
last.label = `${last.label ?? "sensitive"},${span.label ?? "sensitive"}`;
|
|
26663
|
+
}
|
|
26664
|
+
}
|
|
26665
|
+
return merged;
|
|
26666
|
+
}
|
|
26667
|
+
function shannonEntropy(input2) {
|
|
26668
|
+
if (!input2) {
|
|
26669
|
+
return 0;
|
|
26670
|
+
}
|
|
26671
|
+
const counts = /* @__PURE__ */ new Map();
|
|
26672
|
+
for (const char of input2) {
|
|
26673
|
+
counts.set(char, (counts.get(char) ?? 0) + 1);
|
|
26674
|
+
}
|
|
26675
|
+
let entropy = 0;
|
|
26676
|
+
for (const count of counts.values()) {
|
|
26677
|
+
const p = count / input2.length;
|
|
26678
|
+
entropy -= p * Math.log2(p);
|
|
26679
|
+
}
|
|
26680
|
+
return entropy;
|
|
26681
|
+
}
|
|
26682
|
+
function luhn(input2) {
|
|
26683
|
+
if (input2.length < 13 || input2.length > 19) {
|
|
26684
|
+
return false;
|
|
26685
|
+
}
|
|
26686
|
+
let sum = 0;
|
|
26687
|
+
let doubleDigit = false;
|
|
26688
|
+
for (let index = input2.length - 1; index >= 0; index -= 1) {
|
|
26689
|
+
let digit = Number(input2[index]);
|
|
26690
|
+
if (!Number.isInteger(digit)) {
|
|
26691
|
+
return false;
|
|
26692
|
+
}
|
|
26693
|
+
if (doubleDigit) {
|
|
26694
|
+
digit *= 2;
|
|
26695
|
+
if (digit > 9) {
|
|
26696
|
+
digit -= 9;
|
|
26697
|
+
}
|
|
26698
|
+
}
|
|
26699
|
+
sum += digit;
|
|
26700
|
+
doubleDigit = !doubleDigit;
|
|
26701
|
+
}
|
|
26702
|
+
return sum % 10 === 0;
|
|
26703
|
+
}
|
|
26704
|
+
|
|
26483
26705
|
// ../policy/dist/schema.js
|
|
26484
|
-
var actionSchema = external_exports.enum(["allow", "block", "sanitize", "redact_spans", "require_approval", "log_only"]);
|
|
26706
|
+
var actionSchema = external_exports.enum(["allow", "block", "sanitize", "redact_spans", "redact_secrets", "require_approval", "log_only"]);
|
|
26485
26707
|
var directionSchema = external_exports.enum(["request", "response"]);
|
|
26486
26708
|
var trustSchema2 = external_exports.enum(["trusted", "semi", "untrusted"]);
|
|
26487
26709
|
var toolClassSchema2 = external_exports.enum(["source", "sink", "pure", "unknown"]);
|
|
@@ -26490,6 +26712,7 @@ var capabilitySchema2 = external_exports.enum([
|
|
|
26490
26712
|
"reads_untrusted_content",
|
|
26491
26713
|
"reads_sensitive_data",
|
|
26492
26714
|
"network_egress",
|
|
26715
|
+
"file_write",
|
|
26493
26716
|
"writes_local",
|
|
26494
26717
|
"writes_remote",
|
|
26495
26718
|
"deletes_data",
|
|
@@ -26512,7 +26735,12 @@ var policyConditionSchema = external_exports.object({
|
|
|
26512
26735
|
capabilities_all: external_exports.array(capabilitySchema2).optional(),
|
|
26513
26736
|
trust: singleOrArray(trustSchema2).optional(),
|
|
26514
26737
|
taint: external_exports.boolean().optional(),
|
|
26738
|
+
sensitive_taint: external_exports.boolean().optional(),
|
|
26515
26739
|
temporal_taint: external_exports.boolean().optional(),
|
|
26740
|
+
secret_detected: external_exports.boolean().optional(),
|
|
26741
|
+
pii_detected: external_exports.boolean().optional(),
|
|
26742
|
+
destination_allowed: external_exports.boolean().optional(),
|
|
26743
|
+
destination_allowlist_configured: external_exports.boolean().optional(),
|
|
26516
26744
|
detector_score_gte: external_exports.number().min(0).max(1).optional(),
|
|
26517
26745
|
detector_score_lt: external_exports.number().min(0).max(1).optional(),
|
|
26518
26746
|
labels_any: external_exports.array(external_exports.string()).optional(),
|
|
@@ -26602,8 +26830,18 @@ function matchesCondition(condition, ctx) {
|
|
|
26602
26830
|
return false;
|
|
26603
26831
|
if (condition.taint !== void 0 && Boolean(ctx.taint) !== condition.taint)
|
|
26604
26832
|
return false;
|
|
26833
|
+
if (condition.sensitive_taint !== void 0 && Boolean(ctx.sensitive_taint) !== condition.sensitive_taint)
|
|
26834
|
+
return false;
|
|
26605
26835
|
if (condition.temporal_taint !== void 0 && Boolean(ctx.temporal_taint) !== condition.temporal_taint)
|
|
26606
26836
|
return false;
|
|
26837
|
+
if (condition.secret_detected !== void 0 && Boolean(ctx.secret_detected) !== condition.secret_detected)
|
|
26838
|
+
return false;
|
|
26839
|
+
if (condition.pii_detected !== void 0 && Boolean(ctx.pii_detected) !== condition.pii_detected)
|
|
26840
|
+
return false;
|
|
26841
|
+
if (condition.destination_allowed !== void 0 && Boolean(ctx.destination_allowed) !== condition.destination_allowed)
|
|
26842
|
+
return false;
|
|
26843
|
+
if (condition.destination_allowlist_configured !== void 0 && Boolean(ctx.destination_allowlist_configured) !== condition.destination_allowlist_configured)
|
|
26844
|
+
return false;
|
|
26607
26845
|
if (condition.session_quarantined !== void 0 && Boolean(ctx.session_quarantined) !== condition.session_quarantined)
|
|
26608
26846
|
return false;
|
|
26609
26847
|
if (condition.detector_score_gte !== void 0 && (ctx.detector_score ?? 0) < condition.detector_score_gte)
|
|
@@ -26703,14 +26941,44 @@ var InterceptionEngine = class {
|
|
|
26703
26941
|
const toolClass = classification.toolClass;
|
|
26704
26942
|
const argumentText = flattenArguments(params.arguments);
|
|
26705
26943
|
const argumentFields = extractArgumentFields(params.arguments);
|
|
26944
|
+
const argumentBlocks = extractTextBlocks(params.arguments);
|
|
26945
|
+
const argumentDetections = await Promise.all(argumentBlocks.map(async (block) => ({
|
|
26946
|
+
block,
|
|
26947
|
+
detection: await this.options.detector.detect(block.text, {
|
|
26948
|
+
server: this.options.serverName,
|
|
26949
|
+
tool,
|
|
26950
|
+
trust: this.options.server.trust,
|
|
26951
|
+
surface: "argument"
|
|
26952
|
+
})
|
|
26953
|
+
})));
|
|
26954
|
+
const argumentDetection = fuseDetections(argumentDetections.map((entry) => entry.detection));
|
|
26955
|
+
const argumentDetectionsByPath = new Map(argumentDetections.map((entry) => [pathKey(entry.block), entry.detection]));
|
|
26706
26956
|
const matches = this.options.taintStore.match(this.options.sessionId, argumentText, {
|
|
26707
26957
|
fuzzyHammingMax: this.options.config.taint.fuzzyHammingMax
|
|
26708
26958
|
});
|
|
26709
26959
|
const fieldMatches = argumentFields.flatMap((field) => this.options.taintStore.match(this.options.sessionId, field.text, {
|
|
26710
26960
|
fuzzyHammingMax: this.options.config.taint.fuzzyHammingMax
|
|
26711
26961
|
}).map((match) => ({ field, match })));
|
|
26962
|
+
const sensitiveMatches = this.options.taintStore.match(this.options.sessionId, argumentText, {
|
|
26963
|
+
fuzzyHammingMax: this.options.config.taint.fuzzyHammingMax,
|
|
26964
|
+
classes: ["sensitive"]
|
|
26965
|
+
});
|
|
26966
|
+
const sensitiveFieldMatches = argumentFields.flatMap((field) => this.options.taintStore.match(this.options.sessionId, field.text, {
|
|
26967
|
+
fuzzyHammingMax: this.options.config.taint.fuzzyHammingMax,
|
|
26968
|
+
classes: ["sensitive"]
|
|
26969
|
+
}).map((match) => ({ field, match })));
|
|
26712
26970
|
const taintedArgumentRoles = [...new Set(fieldMatches.map(({ field }) => field.role))];
|
|
26713
26971
|
const temporal = matches.some((match) => match.reason === "temporal") || this.options.taintStore.hasTemporal(this.options.sessionId);
|
|
26972
|
+
const secretDetected = hasSecretLabel(argumentDetection.labels);
|
|
26973
|
+
const piiDetected = hasPiiLabel(argumentDetection.labels);
|
|
26974
|
+
const sensitiveTaint = sensitiveMatches.length > 0 || sensitiveFieldMatches.length > 0;
|
|
26975
|
+
const destination = summarizeDestinations(argumentFields, this.options.config.egress.allowlist);
|
|
26976
|
+
const allTaintMatches = [
|
|
26977
|
+
...matches,
|
|
26978
|
+
...fieldMatches.map(({ match }) => match),
|
|
26979
|
+
...sensitiveMatches,
|
|
26980
|
+
...sensitiveFieldMatches.map(({ match }) => match)
|
|
26981
|
+
];
|
|
26714
26982
|
const decision = evaluatePolicy(this.options.policy, {
|
|
26715
26983
|
direction: "request",
|
|
26716
26984
|
method: message.method,
|
|
@@ -26720,7 +26988,14 @@ var InterceptionEngine = class {
|
|
|
26720
26988
|
capabilities: classification.capabilities,
|
|
26721
26989
|
trust: this.options.server.trust,
|
|
26722
26990
|
taint: matches.length > 0 || fieldMatches.length > 0,
|
|
26991
|
+
sensitive_taint: sensitiveTaint,
|
|
26723
26992
|
temporal_taint: temporal,
|
|
26993
|
+
secret_detected: secretDetected,
|
|
26994
|
+
pii_detected: piiDetected,
|
|
26995
|
+
destination_allowed: destination.allowed,
|
|
26996
|
+
destination_allowlist_configured: destination.allowlistConfigured,
|
|
26997
|
+
detector_score: argumentDetection.score,
|
|
26998
|
+
labels: argumentDetection.labels,
|
|
26724
26999
|
argument_text: argumentText,
|
|
26725
27000
|
argument_roles: argumentRolesSummary(argumentFields),
|
|
26726
27001
|
tainted_argument_roles: taintedArgumentRoles
|
|
@@ -26730,29 +27005,49 @@ var InterceptionEngine = class {
|
|
|
26730
27005
|
tool,
|
|
26731
27006
|
toolClass,
|
|
26732
27007
|
classification,
|
|
26733
|
-
taintMatches:
|
|
27008
|
+
taintMatches: allTaintMatches,
|
|
26734
27009
|
summary: `${tool} (${toolClass}; ${classification.capabilities.join(",") || "no capabilities"}) wants to run with ${matches.length + fieldMatches.length} taint match(es).`
|
|
26735
27010
|
});
|
|
26736
27011
|
await this.auditDecision(message, "request", approval.decision, startedAt, {
|
|
26737
27012
|
tool,
|
|
26738
27013
|
toolClass,
|
|
26739
27014
|
classification,
|
|
26740
|
-
|
|
26741
|
-
|
|
27015
|
+
detector: argumentDetection,
|
|
27016
|
+
taintMatches: allTaintMatches,
|
|
27017
|
+
approved: approval.approved,
|
|
27018
|
+
argumentRoles: argumentRolesSummary(argumentFields),
|
|
27019
|
+
taintedArgumentRoles,
|
|
27020
|
+
destination,
|
|
27021
|
+
sensitiveTaint,
|
|
27022
|
+
secretDetected,
|
|
27023
|
+
piiDetected,
|
|
27024
|
+
taintClasses: classesFromMatches(allTaintMatches),
|
|
27025
|
+
redacted: decision.action === "redact_secrets"
|
|
26742
27026
|
});
|
|
26743
27027
|
this.options.taintStore.consumeTurn(this.options.sessionId);
|
|
26744
27028
|
if (!approval.approved || decision.action === "block") {
|
|
27029
|
+
const taintMatchCount = matches.length + fieldMatches.length;
|
|
26745
27030
|
return {
|
|
26746
27031
|
toClient: message.id === void 0 ? [] : [
|
|
26747
|
-
makeErrorResponse(message.id, -32020,
|
|
26748
|
-
decision,
|
|
26749
|
-
taint: matches,
|
|
26750
|
-
taintedArgumentRoles
|
|
26751
|
-
})
|
|
27032
|
+
makeErrorResponse(message.id, -32020, formatBlockedToolCallMessage(decision), makeBlockedToolCallData(decision, taintedArgumentRoles, taintMatchCount))
|
|
26752
27033
|
],
|
|
26753
27034
|
toServer: []
|
|
26754
27035
|
};
|
|
26755
27036
|
}
|
|
27037
|
+
if (decision.action === "redact_secrets") {
|
|
27038
|
+
const redactedArguments = applyTextTransforms(params.arguments, (text, block) => maskSensitiveText(text, argumentDetectionsByPath.get(pathKey(block))?.spans));
|
|
27039
|
+
this.recordPending(message, tool);
|
|
27040
|
+
return {
|
|
27041
|
+
toClient: [],
|
|
27042
|
+
toServer: [{
|
|
27043
|
+
...message,
|
|
27044
|
+
params: {
|
|
27045
|
+
...params,
|
|
27046
|
+
arguments: redactedArguments
|
|
27047
|
+
}
|
|
27048
|
+
}]
|
|
27049
|
+
};
|
|
27050
|
+
}
|
|
26756
27051
|
this.recordPending(message, tool);
|
|
26757
27052
|
return { toClient: [], toServer: [message] };
|
|
26758
27053
|
}
|
|
@@ -26855,6 +27150,9 @@ var InterceptionEngine = class {
|
|
|
26855
27150
|
capabilities: classification.capabilities,
|
|
26856
27151
|
trust: this.options.server.trust,
|
|
26857
27152
|
taint: taintRecords.length > 0,
|
|
27153
|
+
sensitive_taint: recordsHaveClass(taintRecords, "sensitive"),
|
|
27154
|
+
secret_detected: hasSecretLabel(fused.labels),
|
|
27155
|
+
pii_detected: hasPiiLabel(fused.labels),
|
|
26858
27156
|
detector_score: fused.score,
|
|
26859
27157
|
labels: fused.labels
|
|
26860
27158
|
});
|
|
@@ -26873,6 +27171,10 @@ var InterceptionEngine = class {
|
|
|
26873
27171
|
classification,
|
|
26874
27172
|
detector: fused,
|
|
26875
27173
|
taintIds: taintRecords.map((record2) => record2.id),
|
|
27174
|
+
taintClasses: classesFromRecords(taintRecords),
|
|
27175
|
+
sensitiveTaint: recordsHaveClass(taintRecords, "sensitive"),
|
|
27176
|
+
secretDetected: hasSecretLabel(fused.labels),
|
|
27177
|
+
piiDetected: hasPiiLabel(fused.labels),
|
|
26876
27178
|
approved: approval.approved,
|
|
26877
27179
|
payload: result
|
|
26878
27180
|
});
|
|
@@ -26901,7 +27203,7 @@ var InterceptionEngine = class {
|
|
|
26901
27203
|
toServer: []
|
|
26902
27204
|
};
|
|
26903
27205
|
}
|
|
26904
|
-
if (decision.action === "redact_spans") {
|
|
27206
|
+
if (decision.action === "redact_spans" || decision.action === "redact_secrets") {
|
|
26905
27207
|
return {
|
|
26906
27208
|
toClient: [{
|
|
26907
27209
|
...message,
|
|
@@ -26978,8 +27280,10 @@ var InterceptionEngine = class {
|
|
|
26978
27280
|
return { toClient: [message], toServer: [] };
|
|
26979
27281
|
}
|
|
26980
27282
|
registerTaint(tool, toolClass, blocks, detection) {
|
|
26981
|
-
const
|
|
26982
|
-
|
|
27283
|
+
const untrusted = this.options.server.trust !== "trusted" || toolClass === "source" || detection.score >= this.options.config.taint.suspiciousScore;
|
|
27284
|
+
const sensitive = this.isSensitiveOrigin(tool) || hasSecretLabel(detection.labels) || hasPiiLabel(detection.labels);
|
|
27285
|
+
const classes = taintClasses({ untrusted, sensitive });
|
|
27286
|
+
if (classes.length === 0) {
|
|
26983
27287
|
return [];
|
|
26984
27288
|
}
|
|
26985
27289
|
return blocks.filter((block) => block.text.trim().length >= 8).map((block) => this.options.taintStore.add({
|
|
@@ -26989,24 +27293,30 @@ var InterceptionEngine = class {
|
|
|
26989
27293
|
trust: this.options.server.trust,
|
|
26990
27294
|
text: block.text,
|
|
26991
27295
|
detectorScore: detection.score,
|
|
26992
|
-
labels: detection.labels
|
|
27296
|
+
labels: detection.labels,
|
|
27297
|
+
classes
|
|
26993
27298
|
}));
|
|
26994
27299
|
}
|
|
26995
27300
|
registerTaintFromContents(sourceName, classification, contents, detection) {
|
|
26996
27301
|
const highRiskSource = this.options.server.trust === "untrusted" || classification.toolClass === "source" || classification.capabilities.includes("reads_untrusted_content");
|
|
26997
|
-
|
|
26998
|
-
|
|
26999
|
-
|
|
27000
|
-
|
|
27001
|
-
|
|
27002
|
-
|
|
27003
|
-
|
|
27004
|
-
|
|
27005
|
-
|
|
27006
|
-
|
|
27007
|
-
|
|
27008
|
-
|
|
27009
|
-
|
|
27302
|
+
return contents.filter((content) => content.text && content.text.trim().length >= 8 && content.kind !== "binary").flatMap((content) => {
|
|
27303
|
+
const untrusted = highRiskSource || detection.score >= this.options.config.taint.suspiciousScore;
|
|
27304
|
+
const sensitive = this.isSensitiveOrigin(sourceName, content) || hasSecretLabel(detection.labels) || hasPiiLabel(detection.labels);
|
|
27305
|
+
const classes = taintClasses({ untrusted, sensitive });
|
|
27306
|
+
if (classes.length === 0) {
|
|
27307
|
+
return [];
|
|
27308
|
+
}
|
|
27309
|
+
return [this.options.taintStore.add({
|
|
27310
|
+
sessionId: this.options.sessionId,
|
|
27311
|
+
sourceServer: this.options.serverName,
|
|
27312
|
+
sourceTool: content.sourceToolOrResource ?? sourceName,
|
|
27313
|
+
trust: this.options.server.trust,
|
|
27314
|
+
text: content.text ?? "",
|
|
27315
|
+
detectorScore: detection.score,
|
|
27316
|
+
labels: detection.labels,
|
|
27317
|
+
classes
|
|
27318
|
+
})];
|
|
27319
|
+
});
|
|
27010
27320
|
}
|
|
27011
27321
|
async handleDescriptorListResponse(message, bucket, origin, resultKey, nameOf) {
|
|
27012
27322
|
const startedAt = Date.now();
|
|
@@ -27077,6 +27387,9 @@ var InterceptionEngine = class {
|
|
|
27077
27387
|
capabilities: classification.capabilities,
|
|
27078
27388
|
trust: this.options.server.trust,
|
|
27079
27389
|
taint: taintRecords.length > 0,
|
|
27390
|
+
sensitive_taint: recordsHaveClass(taintRecords, "sensitive"),
|
|
27391
|
+
secret_detected: hasSecretLabel(fused.labels),
|
|
27392
|
+
pii_detected: hasPiiLabel(fused.labels),
|
|
27080
27393
|
detector_score: fused.score,
|
|
27081
27394
|
labels: fused.labels
|
|
27082
27395
|
});
|
|
@@ -27093,6 +27406,10 @@ var InterceptionEngine = class {
|
|
|
27093
27406
|
classification,
|
|
27094
27407
|
detector: fused,
|
|
27095
27408
|
taintIds: taintRecords.map((record2) => record2.id),
|
|
27409
|
+
taintClasses: classesFromRecords(taintRecords),
|
|
27410
|
+
sensitiveTaint: recordsHaveClass(taintRecords, "sensitive"),
|
|
27411
|
+
secretDetected: hasSecretLabel(fused.labels),
|
|
27412
|
+
piiDetected: hasPiiLabel(fused.labels),
|
|
27096
27413
|
approved: approval.approved,
|
|
27097
27414
|
payload: result
|
|
27098
27415
|
});
|
|
@@ -27115,7 +27432,7 @@ var InterceptionEngine = class {
|
|
|
27115
27432
|
toServer: []
|
|
27116
27433
|
};
|
|
27117
27434
|
}
|
|
27118
|
-
if (decision.action === "redact_spans") {
|
|
27435
|
+
if (decision.action === "redact_spans" || decision.action === "redact_secrets") {
|
|
27119
27436
|
return {
|
|
27120
27437
|
toClient: [{
|
|
27121
27438
|
...message,
|
|
@@ -27226,7 +27543,7 @@ var InterceptionEngine = class {
|
|
|
27226
27543
|
tool: extra.tool,
|
|
27227
27544
|
direction,
|
|
27228
27545
|
method: extra.method ?? (isRequest(message) ? message.method : void 0),
|
|
27229
|
-
taint_ids: extra.taintIds ?? extra.taintMatches?.map((match) => match.taintId) ?? [],
|
|
27546
|
+
taint_ids: dedupePreservingOrder(extra.taintIds ?? extra.taintMatches?.map((match) => match.taintId) ?? []),
|
|
27230
27547
|
detector: {
|
|
27231
27548
|
score: extra.detector?.score ?? 0,
|
|
27232
27549
|
labels: extra.detector?.labels ?? []
|
|
@@ -27238,13 +27555,46 @@ var InterceptionEngine = class {
|
|
|
27238
27555
|
action: decision.action,
|
|
27239
27556
|
reason: decision.reason,
|
|
27240
27557
|
latency_ms: Date.now() - startedAt,
|
|
27241
|
-
payload: extra.payload,
|
|
27558
|
+
payload: scrubAuditPayload(extra.payload),
|
|
27242
27559
|
metadata: {
|
|
27243
27560
|
toolClass: extra.toolClass,
|
|
27244
27561
|
capabilities: extra.classification?.capabilities,
|
|
27245
27562
|
lockChecks: extra.lockChecks,
|
|
27246
27563
|
lockStatus: extra.lockStatus,
|
|
27247
|
-
approved: extra.approved
|
|
27564
|
+
approved: extra.approved,
|
|
27565
|
+
argumentRoles: extra.argumentRoles,
|
|
27566
|
+
taintedArgumentRoles: extra.taintedArgumentRoles,
|
|
27567
|
+
taintClasses: extra.taintClasses,
|
|
27568
|
+
destination: extra.destination,
|
|
27569
|
+
sensitiveTaint: extra.sensitiveTaint,
|
|
27570
|
+
secretDetected: extra.secretDetected,
|
|
27571
|
+
piiDetected: extra.piiDetected,
|
|
27572
|
+
redacted: extra.redacted
|
|
27573
|
+
}
|
|
27574
|
+
});
|
|
27575
|
+
}
|
|
27576
|
+
isSensitiveOrigin(sourceName, content) {
|
|
27577
|
+
if (this.options.server.sensitive) {
|
|
27578
|
+
return true;
|
|
27579
|
+
}
|
|
27580
|
+
if (this.options.server.sensitiveTools[sourceName] === true) {
|
|
27581
|
+
return true;
|
|
27582
|
+
}
|
|
27583
|
+
if (content?.sourceToolOrResource && this.options.server.sensitiveTools[content.sourceToolOrResource] === true) {
|
|
27584
|
+
return true;
|
|
27585
|
+
}
|
|
27586
|
+
const searchable = [
|
|
27587
|
+
sourceName,
|
|
27588
|
+
content?.sourceToolOrResource,
|
|
27589
|
+
content?.path,
|
|
27590
|
+
typeof content?.rawValue === "string" ? content.rawValue : void 0
|
|
27591
|
+
].filter((value) => Boolean(value));
|
|
27592
|
+
return this.options.server.sensitivePathPatterns.some((pattern) => {
|
|
27593
|
+
try {
|
|
27594
|
+
const regex = new RegExp(pattern, "iu");
|
|
27595
|
+
return searchable.some((value) => regex.test(value));
|
|
27596
|
+
} catch {
|
|
27597
|
+
return false;
|
|
27248
27598
|
}
|
|
27249
27599
|
});
|
|
27250
27600
|
}
|
|
@@ -27252,6 +27602,111 @@ var InterceptionEngine = class {
|
|
|
27252
27602
|
function pathKey(block) {
|
|
27253
27603
|
return block.path.join(".");
|
|
27254
27604
|
}
|
|
27605
|
+
function taintClasses(input2) {
|
|
27606
|
+
const classes = [];
|
|
27607
|
+
if (input2.untrusted) {
|
|
27608
|
+
classes.push("untrusted");
|
|
27609
|
+
}
|
|
27610
|
+
if (input2.sensitive) {
|
|
27611
|
+
classes.push("sensitive");
|
|
27612
|
+
}
|
|
27613
|
+
return classes;
|
|
27614
|
+
}
|
|
27615
|
+
function recordsHaveClass(records, taintClass) {
|
|
27616
|
+
return records.some((record2) => record2.classes.includes(taintClass));
|
|
27617
|
+
}
|
|
27618
|
+
function classesFromRecords(records) {
|
|
27619
|
+
return dedupePreservingOrder(records.flatMap((record2) => record2.classes));
|
|
27620
|
+
}
|
|
27621
|
+
function classesFromMatches(matches) {
|
|
27622
|
+
return dedupePreservingOrder(matches.flatMap((match) => match.classes ?? []));
|
|
27623
|
+
}
|
|
27624
|
+
function summarizeDestinations(argumentFields, allowlist) {
|
|
27625
|
+
const hosts = dedupePreservingOrder(argumentFields.flatMap((field) => hostsFromField(field)));
|
|
27626
|
+
const emails = dedupePreservingOrder(argumentFields.filter((field) => field.role === "email_recipient").map((field) => field.text.toLowerCase()));
|
|
27627
|
+
const allowlistConfigured = allowlist.hosts.length > 0 || allowlist.emails.length > 0;
|
|
27628
|
+
const hostsAllowed = hosts.every((host) => allowlist.hosts.some((entry) => matchesHost(entry, host)));
|
|
27629
|
+
const emailsAllowed = emails.every((email3) => allowlist.emails.some((entry) => matchesEmail(entry, email3)));
|
|
27630
|
+
const hasDestinations = hosts.length > 0 || emails.length > 0;
|
|
27631
|
+
return {
|
|
27632
|
+
allowed: !allowlistConfigured || !hasDestinations || hostsAllowed && emailsAllowed,
|
|
27633
|
+
allowlistConfigured,
|
|
27634
|
+
hosts,
|
|
27635
|
+
emailRecipients: emails.map(maskSensitiveValueForMetadata),
|
|
27636
|
+
destinationCount: hosts.length + emails.length
|
|
27637
|
+
};
|
|
27638
|
+
}
|
|
27639
|
+
function hostsFromField(field) {
|
|
27640
|
+
if (field.role === "hostname") {
|
|
27641
|
+
return [normalizeHost(field.text)].filter(Boolean);
|
|
27642
|
+
}
|
|
27643
|
+
if (field.role !== "url") {
|
|
27644
|
+
return [];
|
|
27645
|
+
}
|
|
27646
|
+
try {
|
|
27647
|
+
return [normalizeHost(new URL(field.text).hostname)].filter(Boolean);
|
|
27648
|
+
} catch {
|
|
27649
|
+
return [];
|
|
27650
|
+
}
|
|
27651
|
+
}
|
|
27652
|
+
function matchesHost(pattern, host) {
|
|
27653
|
+
const normalizedPattern = normalizeHost(pattern);
|
|
27654
|
+
const normalizedHost = normalizeHost(host);
|
|
27655
|
+
if (!normalizedPattern || !normalizedHost) {
|
|
27656
|
+
return false;
|
|
27657
|
+
}
|
|
27658
|
+
if (normalizedPattern === "*") {
|
|
27659
|
+
return true;
|
|
27660
|
+
}
|
|
27661
|
+
if (normalizedPattern.startsWith("*.")) {
|
|
27662
|
+
const suffix = normalizedPattern.slice(1);
|
|
27663
|
+
return normalizedHost.endsWith(suffix);
|
|
27664
|
+
}
|
|
27665
|
+
return normalizedPattern === normalizedHost;
|
|
27666
|
+
}
|
|
27667
|
+
function matchesEmail(pattern, email3) {
|
|
27668
|
+
const normalizedPattern = pattern.trim().toLowerCase();
|
|
27669
|
+
const normalizedEmail = email3.trim().toLowerCase();
|
|
27670
|
+
if (!normalizedPattern || !normalizedEmail) {
|
|
27671
|
+
return false;
|
|
27672
|
+
}
|
|
27673
|
+
if (normalizedPattern === "*") {
|
|
27674
|
+
return true;
|
|
27675
|
+
}
|
|
27676
|
+
if (normalizedPattern.startsWith("*@")) {
|
|
27677
|
+
return normalizedEmail.endsWith(normalizedPattern.slice(1));
|
|
27678
|
+
}
|
|
27679
|
+
if (normalizedPattern.startsWith("@")) {
|
|
27680
|
+
return normalizedEmail.endsWith(normalizedPattern);
|
|
27681
|
+
}
|
|
27682
|
+
return normalizedPattern === normalizedEmail;
|
|
27683
|
+
}
|
|
27684
|
+
function normalizeHost(value) {
|
|
27685
|
+
return value.trim().toLowerCase().replace(/\.$/u, "");
|
|
27686
|
+
}
|
|
27687
|
+
function maskSensitiveValueForMetadata(value) {
|
|
27688
|
+
const [local, domain2] = value.split("@");
|
|
27689
|
+
if (!local || !domain2) {
|
|
27690
|
+
return "[REDACTED:destination]";
|
|
27691
|
+
}
|
|
27692
|
+
return `${local.slice(0, 1) || "*"}***@${domain2}`;
|
|
27693
|
+
}
|
|
27694
|
+
function scrubAuditPayload(value) {
|
|
27695
|
+
if (value === void 0) {
|
|
27696
|
+
return void 0;
|
|
27697
|
+
}
|
|
27698
|
+
if (typeof value === "string") {
|
|
27699
|
+
return maskKnownSensitiveText(value);
|
|
27700
|
+
}
|
|
27701
|
+
if (value === null || typeof value !== "object") {
|
|
27702
|
+
return value;
|
|
27703
|
+
}
|
|
27704
|
+
try {
|
|
27705
|
+
return applyTextTransforms(value, (text) => maskKnownSensitiveText(text));
|
|
27706
|
+
} catch {
|
|
27707
|
+
return "[payload omitted: audit masking failed]";
|
|
27708
|
+
}
|
|
27709
|
+
}
|
|
27255
27710
|
function worstLockStatus(checks) {
|
|
27256
27711
|
if (checks.length === 0)
|
|
27257
27712
|
return "unknown";
|
|
@@ -27274,6 +27729,29 @@ function descriptorName(item, fallback) {
|
|
|
27274
27729
|
}
|
|
27275
27730
|
return `${fallback}:${JSON.stringify(item).slice(0, 80)}`;
|
|
27276
27731
|
}
|
|
27732
|
+
function formatBlockedToolCallMessage(decision) {
|
|
27733
|
+
const reason = trimTrailingPeriod(decision.reason);
|
|
27734
|
+
const rule = decision.matchedRuleId ? ` (rule: ${decision.matchedRuleId})` : "";
|
|
27735
|
+
return `Palizade blocked this call: ${reason}${rule}.`;
|
|
27736
|
+
}
|
|
27737
|
+
function makeBlockedToolCallData(decision, taintedArgumentRoles, taintMatchCount) {
|
|
27738
|
+
return {
|
|
27739
|
+
action: decision.action,
|
|
27740
|
+
rule: {
|
|
27741
|
+
id: decision.matchedRuleId,
|
|
27742
|
+
name: decision.matchedRuleName
|
|
27743
|
+
},
|
|
27744
|
+
reason: decision.reason,
|
|
27745
|
+
taintedArgumentRoles,
|
|
27746
|
+
taintMatchCount
|
|
27747
|
+
};
|
|
27748
|
+
}
|
|
27749
|
+
function trimTrailingPeriod(text) {
|
|
27750
|
+
return text.endsWith(".") ? text.slice(0, -1) : text;
|
|
27751
|
+
}
|
|
27752
|
+
function dedupePreservingOrder(values) {
|
|
27753
|
+
return [...new Set(values)];
|
|
27754
|
+
}
|
|
27277
27755
|
function auditScopeId(scope, profileId, runId, sessionId) {
|
|
27278
27756
|
if (scope === "process") {
|
|
27279
27757
|
return `process:${sessionId}`;
|
|
@@ -27923,6 +28401,7 @@ var SqliteTaintStore = class {
|
|
|
27923
28401
|
payload_hash text not null,
|
|
27924
28402
|
detector_score real not null,
|
|
27925
28403
|
labels_json text not null,
|
|
28404
|
+
classes_json text,
|
|
27926
28405
|
fingerprint_json text not null
|
|
27927
28406
|
);
|
|
27928
28407
|
create index if not exists idx_taint_session on taint_records(session_id);
|
|
@@ -27938,6 +28417,7 @@ var SqliteTaintStore = class {
|
|
|
27938
28417
|
this.ensureColumn("taint_records", "scope_id", "text");
|
|
27939
28418
|
this.ensureColumn("taint_records", "run_id", "text");
|
|
27940
28419
|
this.ensureColumn("taint_records", "expires_at", "text");
|
|
28420
|
+
this.ensureColumn("taint_records", "classes_json", "text");
|
|
27941
28421
|
this.db.exec(`
|
|
27942
28422
|
create index if not exists idx_taint_scope on taint_records(scope_id);
|
|
27943
28423
|
create index if not exists idx_taint_expires on taint_records(expires_at);
|
|
@@ -27962,11 +28442,12 @@ var SqliteTaintStore = class {
|
|
|
27962
28442
|
payloadHash: sha2563(input2.text),
|
|
27963
28443
|
detectorScore: input2.detectorScore,
|
|
27964
28444
|
labels: [...input2.labels],
|
|
28445
|
+
classes: normalizeClasses(input2.classes),
|
|
27965
28446
|
fingerprint: makeProtectedFingerprint(input2.text, this.hmacKey)
|
|
27966
28447
|
};
|
|
27967
28448
|
this.db.prepare(`insert into taint_records
|
|
27968
|
-
(id, profile_id, scope_id, run_id, session_id, source_server, source_tool, trust, created_at, expires_at, payload_hash, detector_score, labels_json, fingerprint_json)
|
|
27969
|
-
values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record2.id, record2.profileId, record2.scopeId, record2.runId ?? null, record2.sessionId, record2.sourceServer, record2.sourceTool, record2.trust, record2.createdAt, record2.expiresAt, record2.payloadHash, record2.detectorScore, JSON.stringify(record2.labels), JSON.stringify(record2.fingerprint));
|
|
28449
|
+
(id, profile_id, scope_id, run_id, session_id, source_server, source_tool, trust, created_at, expires_at, payload_hash, detector_score, labels_json, classes_json, fingerprint_json)
|
|
28450
|
+
values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record2.id, record2.profileId, record2.scopeId, record2.runId ?? null, record2.sessionId, record2.sourceServer, record2.sourceTool, record2.trust, record2.createdAt, record2.expiresAt, record2.payloadHash, record2.detectorScore, JSON.stringify(record2.labels), JSON.stringify(record2.classes), JSON.stringify(record2.fingerprint));
|
|
27970
28451
|
return record2;
|
|
27971
28452
|
}
|
|
27972
28453
|
get(id) {
|
|
@@ -27984,27 +28465,34 @@ var SqliteTaintStore = class {
|
|
|
27984
28465
|
const records = this.recordsForScope(sessionId);
|
|
27985
28466
|
const matches = [];
|
|
27986
28467
|
for (const record2 of records) {
|
|
28468
|
+
if (!matchesClassFilter(record2.classes, options.classes)) {
|
|
28469
|
+
continue;
|
|
28470
|
+
}
|
|
27987
28471
|
const incomingFragments = new Set(incoming.substrings);
|
|
27988
28472
|
const substring = record2.fingerprint.substrings.find((candidate) => candidate.length >= minNormalizedLength && incomingFragments.has(candidate));
|
|
27989
28473
|
if (substring) {
|
|
27990
|
-
matches.push({ taintId: record2.id, reason: "substring", token: substring });
|
|
28474
|
+
matches.push({ taintId: record2.id, reason: "substring", token: substring, classes: record2.classes });
|
|
27991
28475
|
continue;
|
|
27992
28476
|
}
|
|
27993
28477
|
const token = record2.fingerprint.tokens.find((candidate) => candidate.length >= 8 && incoming.tokens.includes(candidate));
|
|
27994
28478
|
if (token) {
|
|
27995
|
-
matches.push({ taintId: record2.id, reason: "token", token });
|
|
28479
|
+
matches.push({ taintId: record2.id, reason: "token", token, classes: record2.classes });
|
|
27996
28480
|
continue;
|
|
27997
28481
|
}
|
|
27998
28482
|
if (record2.fingerprint.normalized.length >= 32 && incoming.normalized.length >= 32) {
|
|
27999
28483
|
const distance = hammingDistanceHex(record2.fingerprint.simhash, incoming.simhash);
|
|
28000
28484
|
if (distance <= fuzzyHammingMax) {
|
|
28001
|
-
matches.push({ taintId: record2.id, reason: "fuzzy", score: 1 - distance / 64 });
|
|
28485
|
+
matches.push({ taintId: record2.id, reason: "fuzzy", classes: record2.classes, score: 1 - distance / 64 });
|
|
28002
28486
|
}
|
|
28003
28487
|
}
|
|
28004
28488
|
}
|
|
28005
28489
|
for (const temporal of this.activeTemporalRows(sessionId)) {
|
|
28006
28490
|
for (const taintId of JSON.parse(temporal.source_taint_ids_json)) {
|
|
28007
|
-
|
|
28491
|
+
const record2 = this.get(taintId);
|
|
28492
|
+
const classes = record2?.classes ?? ["untrusted"];
|
|
28493
|
+
if (matchesClassFilter(classes, options.classes)) {
|
|
28494
|
+
matches.push({ taintId, reason: "temporal", classes });
|
|
28495
|
+
}
|
|
28008
28496
|
}
|
|
28009
28497
|
}
|
|
28010
28498
|
return dedupeMatches(matches);
|
|
@@ -28101,9 +28589,17 @@ function rowToRecord(row) {
|
|
|
28101
28589
|
payloadHash: row.payload_hash,
|
|
28102
28590
|
detectorScore: row.detector_score,
|
|
28103
28591
|
labels: JSON.parse(row.labels_json),
|
|
28592
|
+
classes: row.classes_json ? JSON.parse(row.classes_json) : ["untrusted"],
|
|
28104
28593
|
fingerprint: JSON.parse(row.fingerprint_json)
|
|
28105
28594
|
};
|
|
28106
28595
|
}
|
|
28596
|
+
function normalizeClasses(classes) {
|
|
28597
|
+
const normalized = classes && classes.length > 0 ? classes : ["untrusted"];
|
|
28598
|
+
return [...new Set(normalized)];
|
|
28599
|
+
}
|
|
28600
|
+
function matchesClassFilter(classes, filter) {
|
|
28601
|
+
return !filter || filter.some((taintClass) => classes.includes(taintClass));
|
|
28602
|
+
}
|
|
28107
28603
|
function makeProtectedFingerprint(input2, key) {
|
|
28108
28604
|
const normalized = normalizeText(input2);
|
|
28109
28605
|
const fragments = makeSubstrings(normalized).map((fragment) => hmacSha256Hex(key, fragment));
|
|
@@ -28194,6 +28690,12 @@ function createDetector(config2) {
|
|
|
28194
28690
|
if (config2.detectors.heuristic) {
|
|
28195
28691
|
detectors2.push(new HeuristicDetector());
|
|
28196
28692
|
}
|
|
28693
|
+
if (config2.detectors.secrets.enabled || config2.detectors.pii.enabled) {
|
|
28694
|
+
detectors2.push(new SensitiveDataDetector({
|
|
28695
|
+
secrets: config2.detectors.secrets,
|
|
28696
|
+
pii: config2.detectors.pii
|
|
28697
|
+
}));
|
|
28698
|
+
}
|
|
28197
28699
|
if (config2.detectors.onnxModelPath) {
|
|
28198
28700
|
detectors2.push(new OptionalOnnxDetector({ modelPath: config2.detectors.onnxModelPath }));
|
|
28199
28701
|
}
|
|
@@ -28401,6 +28903,15 @@ detectors:
|
|
|
28401
28903
|
model: sinatras/Llama-Prompt-Guard-2-86M-ONNX
|
|
28402
28904
|
cacheDir: .palizade/models
|
|
28403
28905
|
device: cpu
|
|
28906
|
+
secrets:
|
|
28907
|
+
enabled: false
|
|
28908
|
+
pii:
|
|
28909
|
+
enabled: false
|
|
28910
|
+
|
|
28911
|
+
egress:
|
|
28912
|
+
allowlist:
|
|
28913
|
+
hosts: []
|
|
28914
|
+
emails: []
|
|
28404
28915
|
|
|
28405
28916
|
transport:
|
|
28406
28917
|
maxMessageBytes: 67108864
|
|
@@ -28432,6 +28943,9 @@ servers:
|
|
|
28432
28943
|
read_web: source
|
|
28433
28944
|
send_email: sink
|
|
28434
28945
|
echo: pure
|
|
28946
|
+
sensitive: false
|
|
28947
|
+
sensitiveTools: {}
|
|
28948
|
+
sensitivePathPatterns: []
|
|
28435
28949
|
filesystem:
|
|
28436
28950
|
command: node
|
|
28437
28951
|
args:
|
|
@@ -28453,6 +28967,9 @@ servers:
|
|
|
28453
28967
|
edit_file: sink
|
|
28454
28968
|
create_directory: sink
|
|
28455
28969
|
move_file: sink
|
|
28970
|
+
sensitive: false
|
|
28971
|
+
sensitiveTools: {}
|
|
28972
|
+
sensitivePathPatterns: []
|
|
28456
28973
|
`;
|
|
28457
28974
|
var DEFAULT_POLICY = `version: 1
|
|
28458
28975
|
defaults:
|
|
@@ -28524,7 +29041,6 @@ rules:
|
|
|
28524
29041
|
when:
|
|
28525
29042
|
direction: response
|
|
28526
29043
|
method: tools/call
|
|
28527
|
-
trust: untrusted
|
|
28528
29044
|
detector_score_gte: 0.35
|
|
28529
29045
|
action: sanitize
|
|
28530
29046
|
reason: Untrusted tool output contains injection-like signals.
|
|
@@ -28540,6 +29056,15 @@ rules:
|
|
|
28540
29056
|
action: sanitize
|
|
28541
29057
|
reason: Resource or prompt content contains injection-like signals.
|
|
28542
29058
|
|
|
29059
|
+
- id: sanitize-strong-injection-any-trust
|
|
29060
|
+
name: Spotlight strong injection signals regardless of trust
|
|
29061
|
+
when:
|
|
29062
|
+
direction: response
|
|
29063
|
+
method: tools/call
|
|
29064
|
+
detector_score_gte: 0.75
|
|
29065
|
+
action: sanitize
|
|
29066
|
+
reason: Strong injection signal in tool output.
|
|
29067
|
+
|
|
28543
29068
|
- id: block-tainted-sink
|
|
28544
29069
|
name: Block tainted content entering sinks
|
|
28545
29070
|
when:
|
package/dist/templates.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export declare const DEFAULT_CONFIG = "stateDir: .palizade\npolicy: policies/default.yaml\nlockfile: palizade.lock\n\naudit:\n jsonl: .palizade/audit.jsonl\n sqlite: .palizade/audit.sqlite\n captureRawPayloads: false\n\napprovals:\n mode: localhost\n timeoutMs: 30000\n default: deny\n\ndetectors:\n heuristic: true\n promptGuard2:\n enabled: false\n model: sinatras/Llama-Prompt-Guard-2-86M-ONNX\n cacheDir: .palizade/models\n device: cpu\n\ntransport:\n maxMessageBytes: 67108864\n maxBufferedBytes: 67108864\n allowBatches: false\n allowContentLength: false\n\ntaint:\n sqlite: .palizade/taint.sqlite\n keyPath: .palizade/taint.key\n scope: profile\n profileId: default\n ttlMs: 86400000\n suspiciousScore: 0.35\n fuzzyHammingMax: 7\n temporal:\n enabled: true\n turns: 3\n ttlMs: 300000\n detectorScoreGte: 0.55\n\nservers:\n toy:\n command: node\n args:\n - examples/toy-mcp-server/server.mjs\n trust: untrusted\n toolClasses:\n read_web: source\n send_email: sink\n echo: pure\n filesystem:\n command: node\n args:\n - node_modules/@modelcontextprotocol/server-filesystem/dist/index.js\n - .\n trust: semi\n toolClasses:\n read_file: source\n read_text_file: source\n read_media_file: source\n read_multiple_files: source\n list_directory: source\n list_directory_with_sizes: source\n directory_tree: source\n search_files: source\n get_file_info: source\n list_allowed_directories: source\n write_file: sink\n edit_file: sink\n create_directory: sink\n move_file: sink\n";
|
|
2
|
-
export declare const DEFAULT_POLICY = "version: 1\ndefaults:\n action: allow\n on_error: block\n\nrules:\n - id: deny-server-sampling\n name: Deny server-initiated model access\n when:\n direction: request\n method: sampling/createMessage\n action: block\n reason: MCP server attempted to access the model through sampling.\n\n - id: block-poisoned-tool-metadata\n name: Block poisoned tool metadata\n when:\n direction: response\n method: tools/list\n detector_score_gte: 0.75\n action: block\n reason: Tool metadata looks like prompt injection or tool poisoning.\n\n - id: block-untrusted-unknown-tool\n name: Block unknown tools on untrusted servers\n when:\n direction: request\n method: tools/call\n trust: untrusted\n tool_class: unknown\n action: block\n reason: Unknown tools on untrusted servers must be classified explicitly.\n\n - id: approve-semi-unknown-tool\n name: Require approval for unknown tools on semi-trusted servers\n when:\n direction: request\n method: tools/call\n trust: semi\n tool_class: unknown\n action: require_approval\n reason: Unknown tools on semi-trusted servers require approval.\n\n - id: log-trusted-unknown-tool\n name: Audit unknown tools on trusted servers\n when:\n direction: request\n method: tools/call\n trust: trusted\n tool_class: unknown\n action: log_only\n reason: Unknown tool on trusted server allowed with audit logging.\n\n - id: log-unapproved-tool-metadata\n name: Surface tool lock drift\n when:\n direction: response\n method: tools/list\n lock_status:\n - missing\n - new\n - changed\n action: log_only\n reason: Tool metadata is not approved in palizade.lock.\n\n - id: sanitize-suspicious-untrusted-output\n name: Spotlight suspicious untrusted output\n when:\n direction: response\n method: tools/call\n
|
|
1
|
+
export declare const DEFAULT_CONFIG = "stateDir: .palizade\npolicy: policies/default.yaml\nlockfile: palizade.lock\n\naudit:\n jsonl: .palizade/audit.jsonl\n sqlite: .palizade/audit.sqlite\n captureRawPayloads: false\n\napprovals:\n mode: localhost\n timeoutMs: 30000\n default: deny\n\ndetectors:\n heuristic: true\n promptGuard2:\n enabled: false\n model: sinatras/Llama-Prompt-Guard-2-86M-ONNX\n cacheDir: .palizade/models\n device: cpu\n secrets:\n enabled: false\n pii:\n enabled: false\n\negress:\n allowlist:\n hosts: []\n emails: []\n\ntransport:\n maxMessageBytes: 67108864\n maxBufferedBytes: 67108864\n allowBatches: false\n allowContentLength: false\n\ntaint:\n sqlite: .palizade/taint.sqlite\n keyPath: .palizade/taint.key\n scope: profile\n profileId: default\n ttlMs: 86400000\n suspiciousScore: 0.35\n fuzzyHammingMax: 7\n temporal:\n enabled: true\n turns: 3\n ttlMs: 300000\n detectorScoreGte: 0.55\n\nservers:\n toy:\n command: node\n args:\n - examples/toy-mcp-server/server.mjs\n trust: untrusted\n toolClasses:\n read_web: source\n send_email: sink\n echo: pure\n sensitive: false\n sensitiveTools: {}\n sensitivePathPatterns: []\n filesystem:\n command: node\n args:\n - node_modules/@modelcontextprotocol/server-filesystem/dist/index.js\n - .\n trust: semi\n toolClasses:\n read_file: source\n read_text_file: source\n read_media_file: source\n read_multiple_files: source\n list_directory: source\n list_directory_with_sizes: source\n directory_tree: source\n search_files: source\n get_file_info: source\n list_allowed_directories: source\n write_file: sink\n edit_file: sink\n create_directory: sink\n move_file: sink\n sensitive: false\n sensitiveTools: {}\n sensitivePathPatterns: []\n";
|
|
2
|
+
export declare const DEFAULT_POLICY = "version: 1\ndefaults:\n action: allow\n on_error: block\n\nrules:\n - id: deny-server-sampling\n name: Deny server-initiated model access\n when:\n direction: request\n method: sampling/createMessage\n action: block\n reason: MCP server attempted to access the model through sampling.\n\n - id: block-poisoned-tool-metadata\n name: Block poisoned tool metadata\n when:\n direction: response\n method: tools/list\n detector_score_gte: 0.75\n action: block\n reason: Tool metadata looks like prompt injection or tool poisoning.\n\n - id: block-untrusted-unknown-tool\n name: Block unknown tools on untrusted servers\n when:\n direction: request\n method: tools/call\n trust: untrusted\n tool_class: unknown\n action: block\n reason: Unknown tools on untrusted servers must be classified explicitly.\n\n - id: approve-semi-unknown-tool\n name: Require approval for unknown tools on semi-trusted servers\n when:\n direction: request\n method: tools/call\n trust: semi\n tool_class: unknown\n action: require_approval\n reason: Unknown tools on semi-trusted servers require approval.\n\n - id: log-trusted-unknown-tool\n name: Audit unknown tools on trusted servers\n when:\n direction: request\n method: tools/call\n trust: trusted\n tool_class: unknown\n action: log_only\n reason: Unknown tool on trusted server allowed with audit logging.\n\n - id: log-unapproved-tool-metadata\n name: Surface tool lock drift\n when:\n direction: response\n method: tools/list\n lock_status:\n - missing\n - new\n - changed\n action: log_only\n reason: Tool metadata is not approved in palizade.lock.\n\n - id: sanitize-suspicious-untrusted-output\n name: Spotlight suspicious untrusted output\n when:\n direction: response\n method: tools/call\n detector_score_gte: 0.35\n action: sanitize\n reason: Untrusted tool output contains injection-like signals.\n\n - id: sanitize-suspicious-resource-content\n name: Spotlight suspicious resource content\n when:\n direction: response\n method:\n - resources/read\n - prompts/get\n detector_score_gte: 0.35\n action: sanitize\n reason: Resource or prompt content contains injection-like signals.\n\n - id: sanitize-strong-injection-any-trust\n name: Spotlight strong injection signals regardless of trust\n when:\n direction: response\n method: tools/call\n detector_score_gte: 0.75\n action: sanitize\n reason: Strong injection signal in tool output.\n\n - id: block-tainted-sink\n name: Block tainted content entering sinks\n when:\n direction: request\n method: tools/call\n tool_class: sink\n taint: true\n action: block\n reason: Tainted content is flowing into a sink tool.\n\n - id: block-tainted-egress-destination\n name: Block tainted outbound destinations\n when:\n direction: request\n method: tools/call\n capabilities_any:\n - network_egress\n - sends_message\n tainted_argument_role_any:\n - url\n - hostname\n - email_recipient\n - http_query\n action: block\n reason: Tainted content is being used as an outbound destination or query parameter.\n\n - id: require-approval-temporal-taint-sink\n name: Require approval during temporal taint\n when:\n direction: request\n method: tools/call\n tool_class: sink\n temporal_taint: true\n action: require_approval\n reason: Recent suspicious untrusted content makes sink calls risky.\n";
|
|
3
3
|
//# sourceMappingURL=templates.d.ts.map
|
package/dist/templates.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,21DAwF1B,CAAC;AAEF,eAAO,MAAM,cAAc,soHAiI1B,CAAC"}
|
package/dist/templates.js
CHANGED
|
@@ -19,6 +19,15 @@ detectors:
|
|
|
19
19
|
model: sinatras/Llama-Prompt-Guard-2-86M-ONNX
|
|
20
20
|
cacheDir: .palizade/models
|
|
21
21
|
device: cpu
|
|
22
|
+
secrets:
|
|
23
|
+
enabled: false
|
|
24
|
+
pii:
|
|
25
|
+
enabled: false
|
|
26
|
+
|
|
27
|
+
egress:
|
|
28
|
+
allowlist:
|
|
29
|
+
hosts: []
|
|
30
|
+
emails: []
|
|
22
31
|
|
|
23
32
|
transport:
|
|
24
33
|
maxMessageBytes: 67108864
|
|
@@ -50,6 +59,9 @@ servers:
|
|
|
50
59
|
read_web: source
|
|
51
60
|
send_email: sink
|
|
52
61
|
echo: pure
|
|
62
|
+
sensitive: false
|
|
63
|
+
sensitiveTools: {}
|
|
64
|
+
sensitivePathPatterns: []
|
|
53
65
|
filesystem:
|
|
54
66
|
command: node
|
|
55
67
|
args:
|
|
@@ -71,6 +83,9 @@ servers:
|
|
|
71
83
|
edit_file: sink
|
|
72
84
|
create_directory: sink
|
|
73
85
|
move_file: sink
|
|
86
|
+
sensitive: false
|
|
87
|
+
sensitiveTools: {}
|
|
88
|
+
sensitivePathPatterns: []
|
|
74
89
|
`;
|
|
75
90
|
export const DEFAULT_POLICY = `version: 1
|
|
76
91
|
defaults:
|
|
@@ -142,7 +157,6 @@ rules:
|
|
|
142
157
|
when:
|
|
143
158
|
direction: response
|
|
144
159
|
method: tools/call
|
|
145
|
-
trust: untrusted
|
|
146
160
|
detector_score_gte: 0.35
|
|
147
161
|
action: sanitize
|
|
148
162
|
reason: Untrusted tool output contains injection-like signals.
|
|
@@ -158,6 +172,15 @@ rules:
|
|
|
158
172
|
action: sanitize
|
|
159
173
|
reason: Resource or prompt content contains injection-like signals.
|
|
160
174
|
|
|
175
|
+
- id: sanitize-strong-injection-any-trust
|
|
176
|
+
name: Spotlight strong injection signals regardless of trust
|
|
177
|
+
when:
|
|
178
|
+
direction: response
|
|
179
|
+
method: tools/call
|
|
180
|
+
detector_score_gte: 0.75
|
|
181
|
+
action: sanitize
|
|
182
|
+
reason: Strong injection signal in tool output.
|
|
183
|
+
|
|
161
184
|
- id: block-tainted-sink
|
|
162
185
|
name: Block tainted content entering sinks
|
|
163
186
|
when:
|
package/dist/templates.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,cAAc,GAAG
|
|
1
|
+
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwF7B,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiI7B,CAAC"}
|