@node9/proxy 2.25.1 → 2.26.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 +14 -4
- package/dist/cli.js +3202 -2286
- package/dist/cli.mjs +3440 -2530
- package/dist/dashboard.mjs +12 -8
- package/dist/index.js +57 -10
- package/dist/index.mjs +57 -10
- package/package.json +1 -1
package/dist/dashboard.mjs
CHANGED
|
@@ -1282,19 +1282,19 @@ function resolvePart(p, inQuotes) {
|
|
|
1282
1282
|
}
|
|
1283
1283
|
function analyzeFsOperation(command) {
|
|
1284
1284
|
const normalized = normalizeCommandForPolicy(command);
|
|
1285
|
-
if (!FS_OP_PRESCREEN_RE.test(normalized)) return null;
|
|
1286
|
-
if (fsOpCache.has(
|
|
1287
|
-
const hit = fsOpCache.get(
|
|
1288
|
-
fsOpCache.delete(
|
|
1289
|
-
fsOpCache.set(
|
|
1285
|
+
if (!FS_OP_PRESCREEN_RE.test(normalized) && !FS_OP_PRESCREEN_RE.test(command)) return null;
|
|
1286
|
+
if (fsOpCache.has(command)) {
|
|
1287
|
+
const hit = fsOpCache.get(command) ?? null;
|
|
1288
|
+
fsOpCache.delete(command);
|
|
1289
|
+
fsOpCache.set(command, hit);
|
|
1290
1290
|
return hit;
|
|
1291
1291
|
}
|
|
1292
|
-
const computed = analyzeFsOperationImpl(
|
|
1292
|
+
const computed = analyzeFsOperationImpl(command);
|
|
1293
1293
|
if (fsOpCache.size >= FS_OP_CACHE_MAX) {
|
|
1294
1294
|
const oldest = fsOpCache.keys().next().value;
|
|
1295
1295
|
if (oldest !== void 0) fsOpCache.delete(oldest);
|
|
1296
1296
|
}
|
|
1297
|
-
fsOpCache.set(
|
|
1297
|
+
fsOpCache.set(command, computed);
|
|
1298
1298
|
return computed;
|
|
1299
1299
|
}
|
|
1300
1300
|
function deriveRedirOp(sample) {
|
|
@@ -5638,6 +5638,9 @@ function tryLoadConfig(filePath) {
|
|
|
5638
5638
|
);
|
|
5639
5639
|
}
|
|
5640
5640
|
}
|
|
5641
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
5642
|
+
for (const key of RUNTIME_ONLY_CONFIG_KEYS) delete raw[key];
|
|
5643
|
+
}
|
|
5641
5644
|
const { sanitized, error } = sanitizeConfig(raw);
|
|
5642
5645
|
if (error) {
|
|
5643
5646
|
process.stderr.write(
|
|
@@ -5651,7 +5654,7 @@ ${error.replace("Invalid config:\n", "")}
|
|
|
5651
5654
|
}
|
|
5652
5655
|
return sanitized;
|
|
5653
5656
|
}
|
|
5654
|
-
var DANGEROUS_WORDS, DEFAULT_CONFIG, RM_SAFE_PATH_PATTERN, VERDICT_ORDER, ADVISORY_SMART_RULES, cachedConfig, rejectedApiUrlsSeen, lastParsedRulesCache, CACHE_LOG_REARM_MS, cacheReadLastLoggedAt;
|
|
5657
|
+
var DANGEROUS_WORDS, RUNTIME_ONLY_CONFIG_KEYS, DEFAULT_CONFIG, RM_SAFE_PATH_PATTERN, VERDICT_ORDER, ADVISORY_SMART_RULES, cachedConfig, rejectedApiUrlsSeen, lastParsedRulesCache, CACHE_LOG_REARM_MS, cacheReadLastLoggedAt;
|
|
5655
5658
|
var init_config = __esm({
|
|
5656
5659
|
"src/config/index.ts"() {
|
|
5657
5660
|
"use strict";
|
|
@@ -5669,6 +5672,7 @@ var init_config = __esm({
|
|
|
5669
5672
|
"shred"
|
|
5670
5673
|
// permanently overwrites file contents (unrecoverable)
|
|
5671
5674
|
];
|
|
5675
|
+
RUNTIME_ONLY_CONFIG_KEYS = ["policySource", "ssrfStrictSource"];
|
|
5672
5676
|
DEFAULT_CONFIG = {
|
|
5673
5677
|
version: "1.0",
|
|
5674
5678
|
policySource: "local",
|
package/dist/index.js
CHANGED
|
@@ -3078,19 +3078,19 @@ var FS_OP_CACHE_MAX = 5e3;
|
|
|
3078
3078
|
var fsOpCache = /* @__PURE__ */ new Map();
|
|
3079
3079
|
function analyzeFsOperation(command) {
|
|
3080
3080
|
const normalized = normalizeCommandForPolicy(command);
|
|
3081
|
-
if (!FS_OP_PRESCREEN_RE.test(normalized)) return null;
|
|
3082
|
-
if (fsOpCache.has(
|
|
3083
|
-
const hit = fsOpCache.get(
|
|
3084
|
-
fsOpCache.delete(
|
|
3085
|
-
fsOpCache.set(
|
|
3081
|
+
if (!FS_OP_PRESCREEN_RE.test(normalized) && !FS_OP_PRESCREEN_RE.test(command)) return null;
|
|
3082
|
+
if (fsOpCache.has(command)) {
|
|
3083
|
+
const hit = fsOpCache.get(command) ?? null;
|
|
3084
|
+
fsOpCache.delete(command);
|
|
3085
|
+
fsOpCache.set(command, hit);
|
|
3086
3086
|
return hit;
|
|
3087
3087
|
}
|
|
3088
|
-
const computed = analyzeFsOperationImpl(
|
|
3088
|
+
const computed = analyzeFsOperationImpl(command);
|
|
3089
3089
|
if (fsOpCache.size >= FS_OP_CACHE_MAX) {
|
|
3090
3090
|
const oldest = fsOpCache.keys().next().value;
|
|
3091
3091
|
if (oldest !== void 0) fsOpCache.delete(oldest);
|
|
3092
3092
|
}
|
|
3093
|
-
fsOpCache.set(
|
|
3093
|
+
fsOpCache.set(command, computed);
|
|
3094
3094
|
return computed;
|
|
3095
3095
|
}
|
|
3096
3096
|
var stripDotSlash = (p) => p.replace(/^\.\//, "");
|
|
@@ -6317,6 +6317,7 @@ var DANGEROUS_WORDS = [
|
|
|
6317
6317
|
"shred"
|
|
6318
6318
|
// permanently overwrites file contents (unrecoverable)
|
|
6319
6319
|
];
|
|
6320
|
+
var RUNTIME_ONLY_CONFIG_KEYS = ["policySource", "ssrfStrictSource"];
|
|
6320
6321
|
var DEFAULT_CONFIG = {
|
|
6321
6322
|
version: "1.0",
|
|
6322
6323
|
policySource: "local",
|
|
@@ -7251,6 +7252,9 @@ function tryLoadConfig(filePath) {
|
|
|
7251
7252
|
);
|
|
7252
7253
|
}
|
|
7253
7254
|
}
|
|
7255
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
7256
|
+
for (const key of RUNTIME_ONLY_CONFIG_KEYS) delete raw[key];
|
|
7257
|
+
}
|
|
7254
7258
|
const { sanitized, error } = sanitizeConfig(raw);
|
|
7255
7259
|
if (error) {
|
|
7256
7260
|
process.stderr.write(
|
|
@@ -8383,6 +8387,37 @@ function recordAndCheck(tool, args, threshold = 3, windowMs = 12e4) {
|
|
|
8383
8387
|
}
|
|
8384
8388
|
|
|
8385
8389
|
// src/auth/orchestrator.ts
|
|
8390
|
+
var SENSITIVE_PATH_PATTERN = sensitivePathMatch("").patternName;
|
|
8391
|
+
function isProtectedPathRule(ruleName) {
|
|
8392
|
+
if (!ruleName) return false;
|
|
8393
|
+
return /^(?:org:)?block-path-/.test(ruleName) || /^shield:project-jail:block-read-/.test(ruleName);
|
|
8394
|
+
}
|
|
8395
|
+
function matchedPathField(ruleName) {
|
|
8396
|
+
if (/-anytool-pattern$/.test(ruleName)) return "pattern";
|
|
8397
|
+
if (/-anytool-path$/.test(ruleName)) return "path";
|
|
8398
|
+
if (/-anytool$/.test(ruleName)) return "file_path";
|
|
8399
|
+
return void 0;
|
|
8400
|
+
}
|
|
8401
|
+
function protectedPathOf(ruleName, toolName, args) {
|
|
8402
|
+
const a = args && typeof args === "object" && !Array.isArray(args) ? args : {};
|
|
8403
|
+
if (isBashTool(toolName) && typeof a.command === "string") {
|
|
8404
|
+
return analyzeFsOperation(a.command)?.path || void 0;
|
|
8405
|
+
}
|
|
8406
|
+
const field = matchedPathField(ruleName);
|
|
8407
|
+
const order = field ? [field] : ["file_path", "path", "filename", "pattern"];
|
|
8408
|
+
for (const k of order) {
|
|
8409
|
+
if (typeof a[k] === "string" && a[k]) return a[k];
|
|
8410
|
+
}
|
|
8411
|
+
return void 0;
|
|
8412
|
+
}
|
|
8413
|
+
function safePathForDisplay(p) {
|
|
8414
|
+
return safeMessage(p).replace(/[\u0080-\u009f\u061c\u200e\u200f\u202a-\u202e\u2066-\u2069]/g, "");
|
|
8415
|
+
}
|
|
8416
|
+
function protectedPathFields(ruleName, toolName, args, credentialInArgs) {
|
|
8417
|
+
if (!ruleName || credentialInArgs || !isProtectedPathRule(ruleName)) return {};
|
|
8418
|
+
const p = protectedPathOf(ruleName, toolName, args);
|
|
8419
|
+
return { blockKind: "protected-path", blockedPath: p ? safePathForDisplay(p) : void 0 };
|
|
8420
|
+
}
|
|
8386
8421
|
var WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
8387
8422
|
"write",
|
|
8388
8423
|
"write_file",
|
|
@@ -8675,7 +8710,17 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
8675
8710
|
approved: false,
|
|
8676
8711
|
reason: dlpReason,
|
|
8677
8712
|
blockedBy: "local-config",
|
|
8678
|
-
blockedByLabel: "\u{1F6A8} Node9 DLP (Secret Detected)"
|
|
8713
|
+
blockedByLabel: "\u{1F6A8} Node9 DLP (Secret Detected)",
|
|
8714
|
+
// A PATH hit is a protected file, not a secret in the arguments: the
|
|
8715
|
+
// agent must not be told to rotate a credential it never saw (MSG-1).
|
|
8716
|
+
// Only when the arguments carry no credential: a path match
|
|
8717
|
+
// short-circuits the argument scan above (`scanFilePath ?? scanArgs`),
|
|
8718
|
+
// so a Write of a real key into .env would otherwise be told "nothing
|
|
8719
|
+
// was exposed" (/code-review).
|
|
8720
|
+
...dlpMatch.patternName === SENSITIVE_PATH_PATTERN && !scanArgs(args) && {
|
|
8721
|
+
blockKind: "protected-path",
|
|
8722
|
+
blockedPath: safePathForDisplay(dlpMatch.redactedSample)
|
|
8723
|
+
}
|
|
8679
8724
|
};
|
|
8680
8725
|
}
|
|
8681
8726
|
if (!isManual)
|
|
@@ -8861,7 +8906,8 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
8861
8906
|
blockedByLabel: policyResult.blockedByLabel,
|
|
8862
8907
|
ruleHit: policyResult.ruleName,
|
|
8863
8908
|
...policyResult.recoveryCommand && { recoveryCommand: policyResult.recoveryCommand },
|
|
8864
|
-
...policyResult.ruleDescription && { ruleDescription: policyResult.ruleDescription }
|
|
8909
|
+
...policyResult.ruleDescription && { ruleDescription: policyResult.ruleDescription },
|
|
8910
|
+
...protectedPathFields(policyResult.ruleName, toolName, args, dlpReviewFlagged)
|
|
8865
8911
|
};
|
|
8866
8912
|
};
|
|
8867
8913
|
if (policyResult.dependsOnStatePredicates?.length) {
|
|
@@ -8965,7 +9011,8 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
8965
9011
|
reason: policyResult.reason ?? "Action explicitly blocked by Smart Policy.",
|
|
8966
9012
|
blockedBy: "local-config",
|
|
8967
9013
|
blockedByLabel: policyResult.blockedByLabel,
|
|
8968
|
-
ruleHit: policyResult.ruleName
|
|
9014
|
+
ruleHit: policyResult.ruleName,
|
|
9015
|
+
...protectedPathFields(policyResult.ruleName, toolName, args, dlpReviewFlagged)
|
|
8969
9016
|
};
|
|
8970
9017
|
}
|
|
8971
9018
|
} else {
|
package/dist/index.mjs
CHANGED
|
@@ -3048,19 +3048,19 @@ var FS_OP_CACHE_MAX = 5e3;
|
|
|
3048
3048
|
var fsOpCache = /* @__PURE__ */ new Map();
|
|
3049
3049
|
function analyzeFsOperation(command) {
|
|
3050
3050
|
const normalized = normalizeCommandForPolicy(command);
|
|
3051
|
-
if (!FS_OP_PRESCREEN_RE.test(normalized)) return null;
|
|
3052
|
-
if (fsOpCache.has(
|
|
3053
|
-
const hit = fsOpCache.get(
|
|
3054
|
-
fsOpCache.delete(
|
|
3055
|
-
fsOpCache.set(
|
|
3051
|
+
if (!FS_OP_PRESCREEN_RE.test(normalized) && !FS_OP_PRESCREEN_RE.test(command)) return null;
|
|
3052
|
+
if (fsOpCache.has(command)) {
|
|
3053
|
+
const hit = fsOpCache.get(command) ?? null;
|
|
3054
|
+
fsOpCache.delete(command);
|
|
3055
|
+
fsOpCache.set(command, hit);
|
|
3056
3056
|
return hit;
|
|
3057
3057
|
}
|
|
3058
|
-
const computed = analyzeFsOperationImpl(
|
|
3058
|
+
const computed = analyzeFsOperationImpl(command);
|
|
3059
3059
|
if (fsOpCache.size >= FS_OP_CACHE_MAX) {
|
|
3060
3060
|
const oldest = fsOpCache.keys().next().value;
|
|
3061
3061
|
if (oldest !== void 0) fsOpCache.delete(oldest);
|
|
3062
3062
|
}
|
|
3063
|
-
fsOpCache.set(
|
|
3063
|
+
fsOpCache.set(command, computed);
|
|
3064
3064
|
return computed;
|
|
3065
3065
|
}
|
|
3066
3066
|
var stripDotSlash = (p) => p.replace(/^\.\//, "");
|
|
@@ -6287,6 +6287,7 @@ var DANGEROUS_WORDS = [
|
|
|
6287
6287
|
"shred"
|
|
6288
6288
|
// permanently overwrites file contents (unrecoverable)
|
|
6289
6289
|
];
|
|
6290
|
+
var RUNTIME_ONLY_CONFIG_KEYS = ["policySource", "ssrfStrictSource"];
|
|
6290
6291
|
var DEFAULT_CONFIG = {
|
|
6291
6292
|
version: "1.0",
|
|
6292
6293
|
policySource: "local",
|
|
@@ -7221,6 +7222,9 @@ function tryLoadConfig(filePath) {
|
|
|
7221
7222
|
);
|
|
7222
7223
|
}
|
|
7223
7224
|
}
|
|
7225
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
7226
|
+
for (const key of RUNTIME_ONLY_CONFIG_KEYS) delete raw[key];
|
|
7227
|
+
}
|
|
7224
7228
|
const { sanitized, error } = sanitizeConfig(raw);
|
|
7225
7229
|
if (error) {
|
|
7226
7230
|
process.stderr.write(
|
|
@@ -8353,6 +8357,37 @@ function recordAndCheck(tool, args, threshold = 3, windowMs = 12e4) {
|
|
|
8353
8357
|
}
|
|
8354
8358
|
|
|
8355
8359
|
// src/auth/orchestrator.ts
|
|
8360
|
+
var SENSITIVE_PATH_PATTERN = sensitivePathMatch("").patternName;
|
|
8361
|
+
function isProtectedPathRule(ruleName) {
|
|
8362
|
+
if (!ruleName) return false;
|
|
8363
|
+
return /^(?:org:)?block-path-/.test(ruleName) || /^shield:project-jail:block-read-/.test(ruleName);
|
|
8364
|
+
}
|
|
8365
|
+
function matchedPathField(ruleName) {
|
|
8366
|
+
if (/-anytool-pattern$/.test(ruleName)) return "pattern";
|
|
8367
|
+
if (/-anytool-path$/.test(ruleName)) return "path";
|
|
8368
|
+
if (/-anytool$/.test(ruleName)) return "file_path";
|
|
8369
|
+
return void 0;
|
|
8370
|
+
}
|
|
8371
|
+
function protectedPathOf(ruleName, toolName, args) {
|
|
8372
|
+
const a = args && typeof args === "object" && !Array.isArray(args) ? args : {};
|
|
8373
|
+
if (isBashTool(toolName) && typeof a.command === "string") {
|
|
8374
|
+
return analyzeFsOperation(a.command)?.path || void 0;
|
|
8375
|
+
}
|
|
8376
|
+
const field = matchedPathField(ruleName);
|
|
8377
|
+
const order = field ? [field] : ["file_path", "path", "filename", "pattern"];
|
|
8378
|
+
for (const k of order) {
|
|
8379
|
+
if (typeof a[k] === "string" && a[k]) return a[k];
|
|
8380
|
+
}
|
|
8381
|
+
return void 0;
|
|
8382
|
+
}
|
|
8383
|
+
function safePathForDisplay(p) {
|
|
8384
|
+
return safeMessage(p).replace(/[\u0080-\u009f\u061c\u200e\u200f\u202a-\u202e\u2066-\u2069]/g, "");
|
|
8385
|
+
}
|
|
8386
|
+
function protectedPathFields(ruleName, toolName, args, credentialInArgs) {
|
|
8387
|
+
if (!ruleName || credentialInArgs || !isProtectedPathRule(ruleName)) return {};
|
|
8388
|
+
const p = protectedPathOf(ruleName, toolName, args);
|
|
8389
|
+
return { blockKind: "protected-path", blockedPath: p ? safePathForDisplay(p) : void 0 };
|
|
8390
|
+
}
|
|
8356
8391
|
var WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
8357
8392
|
"write",
|
|
8358
8393
|
"write_file",
|
|
@@ -8645,7 +8680,17 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
8645
8680
|
approved: false,
|
|
8646
8681
|
reason: dlpReason,
|
|
8647
8682
|
blockedBy: "local-config",
|
|
8648
|
-
blockedByLabel: "\u{1F6A8} Node9 DLP (Secret Detected)"
|
|
8683
|
+
blockedByLabel: "\u{1F6A8} Node9 DLP (Secret Detected)",
|
|
8684
|
+
// A PATH hit is a protected file, not a secret in the arguments: the
|
|
8685
|
+
// agent must not be told to rotate a credential it never saw (MSG-1).
|
|
8686
|
+
// Only when the arguments carry no credential: a path match
|
|
8687
|
+
// short-circuits the argument scan above (`scanFilePath ?? scanArgs`),
|
|
8688
|
+
// so a Write of a real key into .env would otherwise be told "nothing
|
|
8689
|
+
// was exposed" (/code-review).
|
|
8690
|
+
...dlpMatch.patternName === SENSITIVE_PATH_PATTERN && !scanArgs(args) && {
|
|
8691
|
+
blockKind: "protected-path",
|
|
8692
|
+
blockedPath: safePathForDisplay(dlpMatch.redactedSample)
|
|
8693
|
+
}
|
|
8649
8694
|
};
|
|
8650
8695
|
}
|
|
8651
8696
|
if (!isManual)
|
|
@@ -8831,7 +8876,8 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
8831
8876
|
blockedByLabel: policyResult.blockedByLabel,
|
|
8832
8877
|
ruleHit: policyResult.ruleName,
|
|
8833
8878
|
...policyResult.recoveryCommand && { recoveryCommand: policyResult.recoveryCommand },
|
|
8834
|
-
...policyResult.ruleDescription && { ruleDescription: policyResult.ruleDescription }
|
|
8879
|
+
...policyResult.ruleDescription && { ruleDescription: policyResult.ruleDescription },
|
|
8880
|
+
...protectedPathFields(policyResult.ruleName, toolName, args, dlpReviewFlagged)
|
|
8835
8881
|
};
|
|
8836
8882
|
};
|
|
8837
8883
|
if (policyResult.dependsOnStatePredicates?.length) {
|
|
@@ -8935,7 +8981,8 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
8935
8981
|
reason: policyResult.reason ?? "Action explicitly blocked by Smart Policy.",
|
|
8936
8982
|
blockedBy: "local-config",
|
|
8937
8983
|
blockedByLabel: policyResult.blockedByLabel,
|
|
8938
|
-
ruleHit: policyResult.ruleName
|
|
8984
|
+
ruleHit: policyResult.ruleName,
|
|
8985
|
+
...protectedPathFields(policyResult.ruleName, toolName, args, dlpReviewFlagged)
|
|
8939
8986
|
};
|
|
8940
8987
|
}
|
|
8941
8988
|
} else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@node9/proxy",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.26.0",
|
|
4
4
|
"description": "IAM for your AI agents. Set what Claude Code, Codex, Gemini, Cursor and any MCP server are allowed to do, review risky actions before they run, and keep every action on the record.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|