@node9/proxy 1.13.1 → 1.14.1
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 +15 -12
- package/dist/cli.js +1610 -1291
- package/dist/cli.mjs +1593 -1274
- package/dist/index.js +80 -6
- package/dist/index.mjs +80 -6
- package/dist/shields/builtin/project-jail.json +64 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -126,6 +126,7 @@ function appendLocalAudit(toolName, args, decision, checkedBy, meta, auditHashAr
|
|
|
126
126
|
...testRun,
|
|
127
127
|
agent: meta?.agent,
|
|
128
128
|
mcpServer: meta?.mcpServer,
|
|
129
|
+
sessionId: meta?.sessionId,
|
|
129
130
|
hostname: import_os.default.hostname()
|
|
130
131
|
});
|
|
131
132
|
}
|
|
@@ -3101,14 +3102,61 @@ var import_fs9 = __toESM(require("fs"));
|
|
|
3101
3102
|
var import_os8 = __toESM(require("os"));
|
|
3102
3103
|
var import_path13 = __toESM(require("path"));
|
|
3103
3104
|
init_audit();
|
|
3104
|
-
|
|
3105
|
-
|
|
3105
|
+
var DLP_SAMPLE_MAX_LEN = 200;
|
|
3106
|
+
var DLP_PATTERN_MAX_LEN = 100;
|
|
3107
|
+
var KNOWN_CHECKED_BY = /* @__PURE__ */ new Set([
|
|
3108
|
+
"dlp-block",
|
|
3109
|
+
"observe-mode-dlp-would-block",
|
|
3110
|
+
"dlp-review-flagged",
|
|
3111
|
+
"loop-detected",
|
|
3112
|
+
"audit-mode",
|
|
3113
|
+
"local-policy",
|
|
3114
|
+
"smart-rule-block",
|
|
3115
|
+
"persistent",
|
|
3116
|
+
"trust",
|
|
3117
|
+
"observe-mode",
|
|
3118
|
+
"observe-mode-would-block"
|
|
3119
|
+
]);
|
|
3120
|
+
function validateApiUrl(raw) {
|
|
3121
|
+
let u;
|
|
3122
|
+
try {
|
|
3123
|
+
u = new URL(raw);
|
|
3124
|
+
} catch {
|
|
3125
|
+
return null;
|
|
3126
|
+
}
|
|
3127
|
+
if (u.username || u.password) return null;
|
|
3128
|
+
if (u.protocol === "https:") return u;
|
|
3129
|
+
if (u.protocol === "http:") {
|
|
3130
|
+
const h = u.hostname;
|
|
3131
|
+
if (h === "127.0.0.1" || h === "localhost" || h === "::1" || h === "[::1]") return u;
|
|
3132
|
+
}
|
|
3133
|
+
return null;
|
|
3134
|
+
}
|
|
3135
|
+
function auditLocalAllow(toolName, args, checkedBy, creds, meta, dlpInfo, containsSensitiveArgs = false) {
|
|
3136
|
+
const validated = validateApiUrl(creds.apiUrl);
|
|
3137
|
+
if (!validated) {
|
|
3138
|
+
try {
|
|
3139
|
+
import_fs9.default.appendFileSync(
|
|
3140
|
+
HOOK_DEBUG_LOG,
|
|
3141
|
+
`[audit] refused to send: invalid apiUrl scheme/host (got "${String(creds.apiUrl).slice(0, 200)}")
|
|
3142
|
+
`
|
|
3143
|
+
);
|
|
3144
|
+
} catch {
|
|
3145
|
+
}
|
|
3146
|
+
return Promise.resolve();
|
|
3147
|
+
}
|
|
3148
|
+
const safeArgs = containsSensitiveArgs ? { tool: toolName, redacted: true } : args;
|
|
3149
|
+
const dlpSample = dlpInfo && typeof dlpInfo.redactedSample === "string" ? dlpInfo.redactedSample.slice(0, DLP_SAMPLE_MAX_LEN) : void 0;
|
|
3150
|
+
const dlpPattern = dlpInfo && typeof dlpInfo.pattern === "string" ? dlpInfo.pattern.slice(0, DLP_PATTERN_MAX_LEN) : void 0;
|
|
3151
|
+
const safeCheckedBy = KNOWN_CHECKED_BY.has(checkedBy) ? checkedBy : "unknown";
|
|
3152
|
+
return fetch(`${validated.toString().replace(/\/$/, "")}/audit`, {
|
|
3106
3153
|
method: "POST",
|
|
3107
3154
|
headers: { "Content-Type": "application/json", Authorization: `Bearer ${creds.apiKey}` },
|
|
3108
3155
|
body: JSON.stringify({
|
|
3109
3156
|
toolName,
|
|
3110
|
-
args,
|
|
3111
|
-
checkedBy,
|
|
3157
|
+
args: safeArgs,
|
|
3158
|
+
checkedBy: safeCheckedBy,
|
|
3159
|
+
...dlpInfo && { dlpPattern, dlpSample },
|
|
3112
3160
|
context: {
|
|
3113
3161
|
agent: meta?.agent,
|
|
3114
3162
|
mcpServer: meta?.mcpServer,
|
|
@@ -3432,6 +3480,16 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
3432
3480
|
meta,
|
|
3433
3481
|
true
|
|
3434
3482
|
);
|
|
3483
|
+
if (approvers.cloud && creds?.apiKey)
|
|
3484
|
+
auditLocalAllow(
|
|
3485
|
+
toolName,
|
|
3486
|
+
args,
|
|
3487
|
+
isObserveMode ? "observe-mode-dlp-would-block" : "dlp-block",
|
|
3488
|
+
creds,
|
|
3489
|
+
meta,
|
|
3490
|
+
{ pattern: dlpMatch.patternName, redactedSample: dlpMatch.redactedSample },
|
|
3491
|
+
true
|
|
3492
|
+
);
|
|
3435
3493
|
if (isWriteTool(toolName) && filePath) {
|
|
3436
3494
|
await notifyTaint(filePath, `DLP:${dlpMatch.patternName}`);
|
|
3437
3495
|
}
|
|
@@ -3500,6 +3558,8 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
3500
3558
|
const reason = `It looks like you've called "${toolName}" ${loopResult.count} times with identical arguments in the last ${ld.windowSeconds}s. Are you stuck? Step back and reconsider your approach \u2014 what are you actually trying to accomplish, and is there a different way to get there?`;
|
|
3501
3559
|
if (!isManual)
|
|
3502
3560
|
appendLocalAudit(toolName, args, "deny", "loop-detected", meta, hashAuditArgs);
|
|
3561
|
+
if (approvers.cloud && creds?.apiKey)
|
|
3562
|
+
auditLocalAllow(toolName, args, "loop-detected", creds, meta, void 0, true);
|
|
3503
3563
|
return {
|
|
3504
3564
|
approved: false,
|
|
3505
3565
|
reason,
|
|
@@ -3586,8 +3646,22 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
3586
3646
|
};
|
|
3587
3647
|
}
|
|
3588
3648
|
} else if (!taintWarning) {
|
|
3589
|
-
|
|
3590
|
-
|
|
3649
|
+
const toolLower = toolName.toLowerCase();
|
|
3650
|
+
const isFileTool = toolLower === "read" || toolLower === "grep" || toolLower === "glob" || toolLower === "read_file" || toolLower === "grep_search" || toolLower === "list_files";
|
|
3651
|
+
if (isFileTool && readActiveShields().includes("project-jail")) {
|
|
3652
|
+
const argsObj = args && typeof args === "object" && !Array.isArray(args) ? args : {};
|
|
3653
|
+
const filePath = String(
|
|
3654
|
+
argsObj.file_path ?? argsObj.path ?? argsObj.pattern ?? argsObj.filename ?? ""
|
|
3655
|
+
);
|
|
3656
|
+
if (filePath && scanFilePath(filePath)) {
|
|
3657
|
+
} else {
|
|
3658
|
+
if (!isManual) appendLocalAudit(toolName, args, "allow", "ignored", meta, hashAuditArgs);
|
|
3659
|
+
return { approved: true };
|
|
3660
|
+
}
|
|
3661
|
+
} else {
|
|
3662
|
+
if (!isManual) appendLocalAudit(toolName, args, "allow", "ignored", meta, hashAuditArgs);
|
|
3663
|
+
return { approved: true };
|
|
3664
|
+
}
|
|
3591
3665
|
}
|
|
3592
3666
|
if (!taintWarning && getActiveTrustSession(toolName, args)) {
|
|
3593
3667
|
if (approvers.cloud && creds?.apiKey)
|
package/dist/index.mjs
CHANGED
|
@@ -106,6 +106,7 @@ function appendLocalAudit(toolName, args, decision, checkedBy, meta, auditHashAr
|
|
|
106
106
|
...testRun,
|
|
107
107
|
agent: meta?.agent,
|
|
108
108
|
mcpServer: meta?.mcpServer,
|
|
109
|
+
sessionId: meta?.sessionId,
|
|
109
110
|
hostname: os.hostname()
|
|
110
111
|
});
|
|
111
112
|
}
|
|
@@ -3071,14 +3072,61 @@ init_audit();
|
|
|
3071
3072
|
import fs9 from "fs";
|
|
3072
3073
|
import os8 from "os";
|
|
3073
3074
|
import path13 from "path";
|
|
3074
|
-
|
|
3075
|
-
|
|
3075
|
+
var DLP_SAMPLE_MAX_LEN = 200;
|
|
3076
|
+
var DLP_PATTERN_MAX_LEN = 100;
|
|
3077
|
+
var KNOWN_CHECKED_BY = /* @__PURE__ */ new Set([
|
|
3078
|
+
"dlp-block",
|
|
3079
|
+
"observe-mode-dlp-would-block",
|
|
3080
|
+
"dlp-review-flagged",
|
|
3081
|
+
"loop-detected",
|
|
3082
|
+
"audit-mode",
|
|
3083
|
+
"local-policy",
|
|
3084
|
+
"smart-rule-block",
|
|
3085
|
+
"persistent",
|
|
3086
|
+
"trust",
|
|
3087
|
+
"observe-mode",
|
|
3088
|
+
"observe-mode-would-block"
|
|
3089
|
+
]);
|
|
3090
|
+
function validateApiUrl(raw) {
|
|
3091
|
+
let u;
|
|
3092
|
+
try {
|
|
3093
|
+
u = new URL(raw);
|
|
3094
|
+
} catch {
|
|
3095
|
+
return null;
|
|
3096
|
+
}
|
|
3097
|
+
if (u.username || u.password) return null;
|
|
3098
|
+
if (u.protocol === "https:") return u;
|
|
3099
|
+
if (u.protocol === "http:") {
|
|
3100
|
+
const h = u.hostname;
|
|
3101
|
+
if (h === "127.0.0.1" || h === "localhost" || h === "::1" || h === "[::1]") return u;
|
|
3102
|
+
}
|
|
3103
|
+
return null;
|
|
3104
|
+
}
|
|
3105
|
+
function auditLocalAllow(toolName, args, checkedBy, creds, meta, dlpInfo, containsSensitiveArgs = false) {
|
|
3106
|
+
const validated = validateApiUrl(creds.apiUrl);
|
|
3107
|
+
if (!validated) {
|
|
3108
|
+
try {
|
|
3109
|
+
fs9.appendFileSync(
|
|
3110
|
+
HOOK_DEBUG_LOG,
|
|
3111
|
+
`[audit] refused to send: invalid apiUrl scheme/host (got "${String(creds.apiUrl).slice(0, 200)}")
|
|
3112
|
+
`
|
|
3113
|
+
);
|
|
3114
|
+
} catch {
|
|
3115
|
+
}
|
|
3116
|
+
return Promise.resolve();
|
|
3117
|
+
}
|
|
3118
|
+
const safeArgs = containsSensitiveArgs ? { tool: toolName, redacted: true } : args;
|
|
3119
|
+
const dlpSample = dlpInfo && typeof dlpInfo.redactedSample === "string" ? dlpInfo.redactedSample.slice(0, DLP_SAMPLE_MAX_LEN) : void 0;
|
|
3120
|
+
const dlpPattern = dlpInfo && typeof dlpInfo.pattern === "string" ? dlpInfo.pattern.slice(0, DLP_PATTERN_MAX_LEN) : void 0;
|
|
3121
|
+
const safeCheckedBy = KNOWN_CHECKED_BY.has(checkedBy) ? checkedBy : "unknown";
|
|
3122
|
+
return fetch(`${validated.toString().replace(/\/$/, "")}/audit`, {
|
|
3076
3123
|
method: "POST",
|
|
3077
3124
|
headers: { "Content-Type": "application/json", Authorization: `Bearer ${creds.apiKey}` },
|
|
3078
3125
|
body: JSON.stringify({
|
|
3079
3126
|
toolName,
|
|
3080
|
-
args,
|
|
3081
|
-
checkedBy,
|
|
3127
|
+
args: safeArgs,
|
|
3128
|
+
checkedBy: safeCheckedBy,
|
|
3129
|
+
...dlpInfo && { dlpPattern, dlpSample },
|
|
3082
3130
|
context: {
|
|
3083
3131
|
agent: meta?.agent,
|
|
3084
3132
|
mcpServer: meta?.mcpServer,
|
|
@@ -3402,6 +3450,16 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
3402
3450
|
meta,
|
|
3403
3451
|
true
|
|
3404
3452
|
);
|
|
3453
|
+
if (approvers.cloud && creds?.apiKey)
|
|
3454
|
+
auditLocalAllow(
|
|
3455
|
+
toolName,
|
|
3456
|
+
args,
|
|
3457
|
+
isObserveMode ? "observe-mode-dlp-would-block" : "dlp-block",
|
|
3458
|
+
creds,
|
|
3459
|
+
meta,
|
|
3460
|
+
{ pattern: dlpMatch.patternName, redactedSample: dlpMatch.redactedSample },
|
|
3461
|
+
true
|
|
3462
|
+
);
|
|
3405
3463
|
if (isWriteTool(toolName) && filePath) {
|
|
3406
3464
|
await notifyTaint(filePath, `DLP:${dlpMatch.patternName}`);
|
|
3407
3465
|
}
|
|
@@ -3470,6 +3528,8 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
3470
3528
|
const reason = `It looks like you've called "${toolName}" ${loopResult.count} times with identical arguments in the last ${ld.windowSeconds}s. Are you stuck? Step back and reconsider your approach \u2014 what are you actually trying to accomplish, and is there a different way to get there?`;
|
|
3471
3529
|
if (!isManual)
|
|
3472
3530
|
appendLocalAudit(toolName, args, "deny", "loop-detected", meta, hashAuditArgs);
|
|
3531
|
+
if (approvers.cloud && creds?.apiKey)
|
|
3532
|
+
auditLocalAllow(toolName, args, "loop-detected", creds, meta, void 0, true);
|
|
3473
3533
|
return {
|
|
3474
3534
|
approved: false,
|
|
3475
3535
|
reason,
|
|
@@ -3556,8 +3616,22 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
3556
3616
|
};
|
|
3557
3617
|
}
|
|
3558
3618
|
} else if (!taintWarning) {
|
|
3559
|
-
|
|
3560
|
-
|
|
3619
|
+
const toolLower = toolName.toLowerCase();
|
|
3620
|
+
const isFileTool = toolLower === "read" || toolLower === "grep" || toolLower === "glob" || toolLower === "read_file" || toolLower === "grep_search" || toolLower === "list_files";
|
|
3621
|
+
if (isFileTool && readActiveShields().includes("project-jail")) {
|
|
3622
|
+
const argsObj = args && typeof args === "object" && !Array.isArray(args) ? args : {};
|
|
3623
|
+
const filePath = String(
|
|
3624
|
+
argsObj.file_path ?? argsObj.path ?? argsObj.pattern ?? argsObj.filename ?? ""
|
|
3625
|
+
);
|
|
3626
|
+
if (filePath && scanFilePath(filePath)) {
|
|
3627
|
+
} else {
|
|
3628
|
+
if (!isManual) appendLocalAudit(toolName, args, "allow", "ignored", meta, hashAuditArgs);
|
|
3629
|
+
return { approved: true };
|
|
3630
|
+
}
|
|
3631
|
+
} else {
|
|
3632
|
+
if (!isManual) appendLocalAudit(toolName, args, "allow", "ignored", meta, hashAuditArgs);
|
|
3633
|
+
return { approved: true };
|
|
3634
|
+
}
|
|
3561
3635
|
}
|
|
3562
3636
|
if (!taintWarning && getActiveTrustSession(toolName, args)) {
|
|
3563
3637
|
if (approvers.cloud && creds?.apiKey)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "project-jail",
|
|
3
|
+
"description": "Restricts AI agents from reading sensitive credential files outside the current project",
|
|
4
|
+
"aliases": ["jail"],
|
|
5
|
+
"smartRules": [
|
|
6
|
+
{
|
|
7
|
+
"name": "shield:project-jail:block-read-ssh",
|
|
8
|
+
"tool": "bash",
|
|
9
|
+
"conditions": [
|
|
10
|
+
{
|
|
11
|
+
"field": "command",
|
|
12
|
+
"op": "matches",
|
|
13
|
+
"value": "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type)\\s+.*[\\/\\\\]\\.ssh[\\/\\\\]",
|
|
14
|
+
"flags": "i"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"verdict": "block",
|
|
18
|
+
"reason": "Reading SSH private keys is blocked by project-jail shield"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "shield:project-jail:block-read-aws",
|
|
22
|
+
"tool": "bash",
|
|
23
|
+
"conditions": [
|
|
24
|
+
{
|
|
25
|
+
"field": "command",
|
|
26
|
+
"op": "matches",
|
|
27
|
+
"value": "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type)\\s+.*[\\/\\\\]\\.aws[\\/\\\\]",
|
|
28
|
+
"flags": "i"
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"verdict": "block",
|
|
32
|
+
"reason": "Reading AWS credentials is blocked by project-jail shield"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"name": "shield:project-jail:block-read-env",
|
|
36
|
+
"tool": "bash",
|
|
37
|
+
"conditions": [
|
|
38
|
+
{
|
|
39
|
+
"field": "command",
|
|
40
|
+
"op": "matches",
|
|
41
|
+
"value": "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type)\\s+.*\\.env(\\.local|\\.production|\\.staging)?\\b",
|
|
42
|
+
"flags": "i"
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"verdict": "block",
|
|
46
|
+
"reason": "Reading .env files is blocked by project-jail shield"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"name": "shield:project-jail:block-read-credentials",
|
|
50
|
+
"tool": "bash",
|
|
51
|
+
"conditions": [
|
|
52
|
+
{
|
|
53
|
+
"field": "command",
|
|
54
|
+
"op": "matches",
|
|
55
|
+
"value": "(cat|less|head|tail|bat|more|open|print|nano|vim|vi|emacs|code|type)\\s+.*(credentials\\.json|\\.netrc|\\.npmrc|\\.docker[\\/\\\\]config\\.json|gcloud[\\/\\\\]credentials)",
|
|
56
|
+
"flags": "i"
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
"verdict": "block",
|
|
60
|
+
"reason": "Reading credential files is blocked by project-jail shield"
|
|
61
|
+
}
|
|
62
|
+
],
|
|
63
|
+
"dangerousWords": []
|
|
64
|
+
}
|