@node9/proxy 1.0.19 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/cli.js +518 -235
- package/dist/cli.mjs +524 -241
- package/dist/index.js +197 -54
- package/dist/index.mjs +197 -54
- package/package.json +2 -1
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
// src/core.ts
|
|
2
2
|
import chalk2 from "chalk";
|
|
3
3
|
import { confirm } from "@inquirer/prompts";
|
|
4
|
-
import
|
|
5
|
-
import
|
|
4
|
+
import fs3 from "fs";
|
|
5
|
+
import path5 from "path";
|
|
6
6
|
import os2 from "os";
|
|
7
7
|
import net from "net";
|
|
8
8
|
import { randomUUID } from "crypto";
|
|
9
9
|
import { spawnSync } from "child_process";
|
|
10
10
|
import pm from "picomatch";
|
|
11
|
+
import safeRegex from "safe-regex2";
|
|
11
12
|
import { parse } from "sh-syntax";
|
|
12
13
|
|
|
13
14
|
// src/ui/native.ts
|
|
@@ -429,8 +430,8 @@ function sanitizeConfig(raw) {
|
|
|
429
430
|
}
|
|
430
431
|
}
|
|
431
432
|
const lines = result.error.issues.map((issue) => {
|
|
432
|
-
const
|
|
433
|
-
return ` \u2022 ${
|
|
433
|
+
const path6 = issue.path.length > 0 ? issue.path.join(".") : "root";
|
|
434
|
+
return ` \u2022 ${path6}: ${issue.message}`;
|
|
434
435
|
});
|
|
435
436
|
return {
|
|
436
437
|
sanitized,
|
|
@@ -643,6 +644,8 @@ function readActiveShields() {
|
|
|
643
644
|
}
|
|
644
645
|
|
|
645
646
|
// src/dlp.ts
|
|
647
|
+
import fs2 from "fs";
|
|
648
|
+
import path4 from "path";
|
|
646
649
|
var DLP_PATTERNS = [
|
|
647
650
|
{ name: "AWS Access Key ID", regex: /\bAKIA[0-9A-Z]{16}\b/, severity: "block" },
|
|
648
651
|
{ name: "GitHub Token", regex: /\bgh[pous]_[A-Za-z0-9]{36}\b/, severity: "block" },
|
|
@@ -654,8 +657,76 @@ var DLP_PATTERNS = [
|
|
|
654
657
|
regex: /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/,
|
|
655
658
|
severity: "block"
|
|
656
659
|
},
|
|
660
|
+
// GCP service account JSON (detects the type field that uniquely identifies it)
|
|
661
|
+
{
|
|
662
|
+
name: "GCP Service Account",
|
|
663
|
+
regex: /"type"\s*:\s*"service_account"/,
|
|
664
|
+
severity: "block"
|
|
665
|
+
},
|
|
666
|
+
// NPM auth token in .npmrc format
|
|
667
|
+
{
|
|
668
|
+
name: "NPM Auth Token",
|
|
669
|
+
regex: /_authToken\s*=\s*[A-Za-z0-9_\-]{20,}/,
|
|
670
|
+
severity: "block"
|
|
671
|
+
},
|
|
657
672
|
{ name: "Bearer Token", regex: /Bearer\s+[a-zA-Z0-9\-._~+/]+=*/i, severity: "review" }
|
|
658
673
|
];
|
|
674
|
+
var SENSITIVE_PATH_PATTERNS = [
|
|
675
|
+
/[/\\]\.ssh[/\\]/i,
|
|
676
|
+
/[/\\]\.aws[/\\]/i,
|
|
677
|
+
/[/\\]\.config[/\\]gcloud[/\\]/i,
|
|
678
|
+
/[/\\]\.azure[/\\]/i,
|
|
679
|
+
/[/\\]\.kube[/\\]config$/i,
|
|
680
|
+
/[/\\]\.env($|\.)/i,
|
|
681
|
+
// .env, .env.local, .env.production — not .envoy
|
|
682
|
+
/[/\\]\.git-credentials$/i,
|
|
683
|
+
/[/\\]\.npmrc$/i,
|
|
684
|
+
/[/\\]\.docker[/\\]config\.json$/i,
|
|
685
|
+
/[/\\][^/\\]+\.pem$/i,
|
|
686
|
+
/[/\\][^/\\]+\.key$/i,
|
|
687
|
+
/[/\\][^/\\]+\.p12$/i,
|
|
688
|
+
/[/\\][^/\\]+\.pfx$/i,
|
|
689
|
+
/^\/etc\/passwd$/,
|
|
690
|
+
/^\/etc\/shadow$/,
|
|
691
|
+
/^\/etc\/sudoers$/,
|
|
692
|
+
/[/\\]credentials\.json$/i,
|
|
693
|
+
/[/\\]id_rsa$/i,
|
|
694
|
+
/[/\\]id_ed25519$/i,
|
|
695
|
+
/[/\\]id_ecdsa$/i
|
|
696
|
+
];
|
|
697
|
+
function scanFilePath(filePath, cwd = process.cwd()) {
|
|
698
|
+
if (!filePath) return null;
|
|
699
|
+
let resolved;
|
|
700
|
+
try {
|
|
701
|
+
const absolute = path4.resolve(cwd, filePath);
|
|
702
|
+
resolved = fs2.realpathSync.native(absolute);
|
|
703
|
+
} catch (err) {
|
|
704
|
+
const code = err.code;
|
|
705
|
+
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
706
|
+
resolved = path4.resolve(cwd, filePath);
|
|
707
|
+
} else {
|
|
708
|
+
return {
|
|
709
|
+
patternName: "Sensitive File Path",
|
|
710
|
+
fieldPath: "file_path",
|
|
711
|
+
redactedSample: filePath,
|
|
712
|
+
severity: "block"
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
const normalised = resolved.replace(/\\/g, "/");
|
|
717
|
+
for (const pattern of SENSITIVE_PATH_PATTERNS) {
|
|
718
|
+
if (pattern.test(normalised)) {
|
|
719
|
+
return {
|
|
720
|
+
patternName: "Sensitive File Path",
|
|
721
|
+
fieldPath: "file_path",
|
|
722
|
+
redactedSample: filePath,
|
|
723
|
+
// show original path in alert, not resolved
|
|
724
|
+
severity: "block"
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
return null;
|
|
729
|
+
}
|
|
659
730
|
function maskSecret(raw, pattern) {
|
|
660
731
|
const match = raw.match(pattern);
|
|
661
732
|
if (!match) return "****";
|
|
@@ -713,17 +784,17 @@ function scanArgs(args, depth = 0, fieldPath = "args") {
|
|
|
713
784
|
}
|
|
714
785
|
|
|
715
786
|
// src/core.ts
|
|
716
|
-
var PAUSED_FILE =
|
|
717
|
-
var TRUST_FILE =
|
|
718
|
-
var LOCAL_AUDIT_LOG =
|
|
719
|
-
var HOOK_DEBUG_LOG =
|
|
787
|
+
var PAUSED_FILE = path5.join(os2.homedir(), ".node9", "PAUSED");
|
|
788
|
+
var TRUST_FILE = path5.join(os2.homedir(), ".node9", "trust.json");
|
|
789
|
+
var LOCAL_AUDIT_LOG = path5.join(os2.homedir(), ".node9", "audit.log");
|
|
790
|
+
var HOOK_DEBUG_LOG = path5.join(os2.homedir(), ".node9", "hook-debug.log");
|
|
720
791
|
function checkPause() {
|
|
721
792
|
try {
|
|
722
|
-
if (!
|
|
723
|
-
const state = JSON.parse(
|
|
793
|
+
if (!fs3.existsSync(PAUSED_FILE)) return { paused: false };
|
|
794
|
+
const state = JSON.parse(fs3.readFileSync(PAUSED_FILE, "utf-8"));
|
|
724
795
|
if (state.expiry > 0 && Date.now() >= state.expiry) {
|
|
725
796
|
try {
|
|
726
|
-
|
|
797
|
+
fs3.unlinkSync(PAUSED_FILE);
|
|
727
798
|
} catch {
|
|
728
799
|
}
|
|
729
800
|
return { paused: false };
|
|
@@ -734,20 +805,93 @@ function checkPause() {
|
|
|
734
805
|
}
|
|
735
806
|
}
|
|
736
807
|
function atomicWriteSync(filePath, data, options) {
|
|
737
|
-
const dir =
|
|
738
|
-
if (!
|
|
808
|
+
const dir = path5.dirname(filePath);
|
|
809
|
+
if (!fs3.existsSync(dir)) fs3.mkdirSync(dir, { recursive: true });
|
|
739
810
|
const tmpPath = `${filePath}.${os2.hostname()}.${process.pid}.tmp`;
|
|
740
|
-
|
|
741
|
-
|
|
811
|
+
fs3.writeFileSync(tmpPath, data, options);
|
|
812
|
+
fs3.renameSync(tmpPath, filePath);
|
|
813
|
+
}
|
|
814
|
+
var MAX_REGEX_LENGTH = 100;
|
|
815
|
+
var REGEX_CACHE_MAX = 500;
|
|
816
|
+
var regexCache = /* @__PURE__ */ new Map();
|
|
817
|
+
function validateRegex(pattern) {
|
|
818
|
+
if (!pattern) return "Pattern is required";
|
|
819
|
+
if (pattern.length > MAX_REGEX_LENGTH) return `Pattern exceeds max length of ${MAX_REGEX_LENGTH}`;
|
|
820
|
+
let parens = 0, brackets = 0, isEscaped = false, inCharClass = false;
|
|
821
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
822
|
+
const char = pattern[i];
|
|
823
|
+
if (isEscaped) {
|
|
824
|
+
isEscaped = false;
|
|
825
|
+
continue;
|
|
826
|
+
}
|
|
827
|
+
if (char === "\\") {
|
|
828
|
+
isEscaped = true;
|
|
829
|
+
continue;
|
|
830
|
+
}
|
|
831
|
+
if (char === "[" && !inCharClass) {
|
|
832
|
+
inCharClass = true;
|
|
833
|
+
brackets++;
|
|
834
|
+
continue;
|
|
835
|
+
}
|
|
836
|
+
if (char === "]" && inCharClass) {
|
|
837
|
+
inCharClass = false;
|
|
838
|
+
brackets--;
|
|
839
|
+
continue;
|
|
840
|
+
}
|
|
841
|
+
if (inCharClass) continue;
|
|
842
|
+
if (char === "(") parens++;
|
|
843
|
+
else if (char === ")") parens--;
|
|
844
|
+
}
|
|
845
|
+
if (parens !== 0) return "Unbalanced parentheses";
|
|
846
|
+
if (brackets !== 0) return "Unbalanced brackets";
|
|
847
|
+
if (/\\\d+[*+{]/.test(pattern)) return "Quantified backreferences are forbidden (ReDoS risk)";
|
|
848
|
+
if (!safeRegex(pattern)) return "Pattern rejected: potential ReDoS vulnerability detected";
|
|
849
|
+
try {
|
|
850
|
+
new RegExp(pattern);
|
|
851
|
+
} catch (e) {
|
|
852
|
+
return `Invalid regex syntax: ${e.message}`;
|
|
853
|
+
}
|
|
854
|
+
return null;
|
|
855
|
+
}
|
|
856
|
+
function getCompiledRegex(pattern, flags = "") {
|
|
857
|
+
if (flags && !/^[gimsuy]+$/.test(flags)) {
|
|
858
|
+
if (process.env.NODE9_DEBUG === "1") console.error(`[Node9] Invalid regex flags: "${flags}"`);
|
|
859
|
+
return null;
|
|
860
|
+
}
|
|
861
|
+
const key = `${pattern}\0${flags}`;
|
|
862
|
+
if (regexCache.has(key)) {
|
|
863
|
+
const cached = regexCache.get(key);
|
|
864
|
+
regexCache.delete(key);
|
|
865
|
+
regexCache.set(key, cached);
|
|
866
|
+
return cached;
|
|
867
|
+
}
|
|
868
|
+
const err = validateRegex(pattern);
|
|
869
|
+
if (err) {
|
|
870
|
+
if (process.env.NODE9_DEBUG === "1")
|
|
871
|
+
console.error(`[Node9] Regex blocked: ${err} \u2014 pattern: "${pattern}"`);
|
|
872
|
+
return null;
|
|
873
|
+
}
|
|
874
|
+
try {
|
|
875
|
+
const re = new RegExp(pattern, flags);
|
|
876
|
+
if (regexCache.size >= REGEX_CACHE_MAX) {
|
|
877
|
+
const oldest = regexCache.keys().next().value;
|
|
878
|
+
if (oldest) regexCache.delete(oldest);
|
|
879
|
+
}
|
|
880
|
+
regexCache.set(key, re);
|
|
881
|
+
return re;
|
|
882
|
+
} catch (e) {
|
|
883
|
+
if (process.env.NODE9_DEBUG === "1") console.error(`[Node9] Regex compile failed:`, e);
|
|
884
|
+
return null;
|
|
885
|
+
}
|
|
742
886
|
}
|
|
743
887
|
function getActiveTrustSession(toolName) {
|
|
744
888
|
try {
|
|
745
|
-
if (!
|
|
746
|
-
const trust = JSON.parse(
|
|
889
|
+
if (!fs3.existsSync(TRUST_FILE)) return false;
|
|
890
|
+
const trust = JSON.parse(fs3.readFileSync(TRUST_FILE, "utf-8"));
|
|
747
891
|
const now = Date.now();
|
|
748
892
|
const active = trust.entries.filter((e) => e.expiry > now);
|
|
749
893
|
if (active.length !== trust.entries.length) {
|
|
750
|
-
|
|
894
|
+
fs3.writeFileSync(TRUST_FILE, JSON.stringify({ entries: active }, null, 2));
|
|
751
895
|
}
|
|
752
896
|
return active.some((e) => e.tool === toolName || matchesPattern(toolName, e.tool));
|
|
753
897
|
} catch {
|
|
@@ -758,8 +902,8 @@ function writeTrustSession(toolName, durationMs) {
|
|
|
758
902
|
try {
|
|
759
903
|
let trust = { entries: [] };
|
|
760
904
|
try {
|
|
761
|
-
if (
|
|
762
|
-
trust = JSON.parse(
|
|
905
|
+
if (fs3.existsSync(TRUST_FILE)) {
|
|
906
|
+
trust = JSON.parse(fs3.readFileSync(TRUST_FILE, "utf-8"));
|
|
763
907
|
}
|
|
764
908
|
} catch {
|
|
765
909
|
}
|
|
@@ -775,9 +919,9 @@ function writeTrustSession(toolName, durationMs) {
|
|
|
775
919
|
}
|
|
776
920
|
function appendToLog(logPath, entry) {
|
|
777
921
|
try {
|
|
778
|
-
const dir =
|
|
779
|
-
if (!
|
|
780
|
-
|
|
922
|
+
const dir = path5.dirname(logPath);
|
|
923
|
+
if (!fs3.existsSync(dir)) fs3.mkdirSync(dir, { recursive: true });
|
|
924
|
+
fs3.appendFileSync(logPath, JSON.stringify(entry) + "\n");
|
|
781
925
|
} catch {
|
|
782
926
|
}
|
|
783
927
|
}
|
|
@@ -819,9 +963,9 @@ function matchesPattern(text, patterns) {
|
|
|
819
963
|
const withoutDotSlash = text.replace(/^\.\//, "");
|
|
820
964
|
return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
|
|
821
965
|
}
|
|
822
|
-
function getNestedValue(obj,
|
|
966
|
+
function getNestedValue(obj, path6) {
|
|
823
967
|
if (!obj || typeof obj !== "object") return null;
|
|
824
|
-
return
|
|
968
|
+
return path6.split(".").reduce((prev, curr) => prev?.[curr], obj);
|
|
825
969
|
}
|
|
826
970
|
function evaluateSmartConditions(args, rule) {
|
|
827
971
|
if (!rule.conditions || rule.conditions.length === 0) return true;
|
|
@@ -840,19 +984,16 @@ function evaluateSmartConditions(args, rule) {
|
|
|
840
984
|
return val !== null && cond.value ? !val.includes(cond.value) : true;
|
|
841
985
|
case "matches": {
|
|
842
986
|
if (val === null || !cond.value) return false;
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
return false;
|
|
847
|
-
}
|
|
987
|
+
const reM = getCompiledRegex(cond.value, cond.flags ?? "");
|
|
988
|
+
if (!reM) return false;
|
|
989
|
+
return reM.test(val);
|
|
848
990
|
}
|
|
849
991
|
case "notMatches": {
|
|
850
|
-
if (
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
}
|
|
992
|
+
if (!cond.value) return false;
|
|
993
|
+
if (val === null) return true;
|
|
994
|
+
const reN = getCompiledRegex(cond.value, cond.flags ?? "");
|
|
995
|
+
if (!reN) return false;
|
|
996
|
+
return !reN.test(val);
|
|
856
997
|
}
|
|
857
998
|
case "matchesGlob":
|
|
858
999
|
return val !== null && cond.value ? pm.isMatch(val, cond.value) : false;
|
|
@@ -1174,9 +1315,9 @@ var ADVISORY_SMART_RULES = [
|
|
|
1174
1315
|
var cachedConfig = null;
|
|
1175
1316
|
function getInternalToken() {
|
|
1176
1317
|
try {
|
|
1177
|
-
const pidFile =
|
|
1178
|
-
if (!
|
|
1179
|
-
const data = JSON.parse(
|
|
1318
|
+
const pidFile = path5.join(os2.homedir(), ".node9", "daemon.pid");
|
|
1319
|
+
if (!fs3.existsSync(pidFile)) return null;
|
|
1320
|
+
const data = JSON.parse(fs3.readFileSync(pidFile, "utf-8"));
|
|
1180
1321
|
process.kill(data.pid, 0);
|
|
1181
1322
|
return data.internalToken ?? null;
|
|
1182
1323
|
} catch {
|
|
@@ -1296,10 +1437,10 @@ function isIgnoredTool(toolName) {
|
|
|
1296
1437
|
var DAEMON_PORT = 7391;
|
|
1297
1438
|
var DAEMON_HOST = "127.0.0.1";
|
|
1298
1439
|
function isDaemonRunning() {
|
|
1299
|
-
const pidFile =
|
|
1300
|
-
if (
|
|
1440
|
+
const pidFile = path5.join(os2.homedir(), ".node9", "daemon.pid");
|
|
1441
|
+
if (fs3.existsSync(pidFile)) {
|
|
1301
1442
|
try {
|
|
1302
|
-
const { pid, port } = JSON.parse(
|
|
1443
|
+
const { pid, port } = JSON.parse(fs3.readFileSync(pidFile, "utf-8"));
|
|
1303
1444
|
if (port !== DAEMON_PORT) return false;
|
|
1304
1445
|
process.kill(pid, 0);
|
|
1305
1446
|
return true;
|
|
@@ -1319,9 +1460,9 @@ function isDaemonRunning() {
|
|
|
1319
1460
|
}
|
|
1320
1461
|
function getPersistentDecision(toolName) {
|
|
1321
1462
|
try {
|
|
1322
|
-
const file =
|
|
1323
|
-
if (!
|
|
1324
|
-
const decisions = JSON.parse(
|
|
1463
|
+
const file = path5.join(os2.homedir(), ".node9", "decisions.json");
|
|
1464
|
+
if (!fs3.existsSync(file)) return null;
|
|
1465
|
+
const decisions = JSON.parse(fs3.readFileSync(file, "utf-8"));
|
|
1325
1466
|
const d = decisions[toolName];
|
|
1326
1467
|
if (d === "allow" || d === "deny") return d;
|
|
1327
1468
|
} catch {
|
|
@@ -1403,7 +1544,7 @@ async function resolveViaDaemon(id, decision, internalToken) {
|
|
|
1403
1544
|
signal: AbortSignal.timeout(3e3)
|
|
1404
1545
|
});
|
|
1405
1546
|
}
|
|
1406
|
-
var ACTIVITY_SOCKET_PATH = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" :
|
|
1547
|
+
var ACTIVITY_SOCKET_PATH = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path5.join(os2.tmpdir(), "node9-activity.sock");
|
|
1407
1548
|
function notifyActivity(data) {
|
|
1408
1549
|
return new Promise((resolve) => {
|
|
1409
1550
|
try {
|
|
@@ -1465,7 +1606,9 @@ async function _authorizeHeadlessCore(toolName, args, allowTerminalFallback = fa
|
|
|
1465
1606
|
let policyMatchedWord;
|
|
1466
1607
|
let riskMetadata;
|
|
1467
1608
|
if (config.policy.dlp.enabled && (!isIgnoredTool(toolName) || config.policy.dlp.scanIgnoredTools)) {
|
|
1468
|
-
const
|
|
1609
|
+
const argsObj = args && typeof args === "object" && !Array.isArray(args) ? args : {};
|
|
1610
|
+
const filePath = String(argsObj.file_path ?? argsObj.path ?? argsObj.filename ?? "");
|
|
1611
|
+
const dlpMatch = (filePath ? scanFilePath(filePath) : null) ?? scanArgs(args);
|
|
1469
1612
|
if (dlpMatch) {
|
|
1470
1613
|
const dlpReason = `\u{1F6A8} DATA LOSS PREVENTION: ${dlpMatch.patternName} detected in field "${dlpMatch.fieldPath}" (${dlpMatch.redactedSample})`;
|
|
1471
1614
|
if (dlpMatch.severity === "block") {
|
|
@@ -1839,8 +1982,8 @@ REASON: Action blocked because no approval channels are available. (Native/Brows
|
|
|
1839
1982
|
}
|
|
1840
1983
|
function getConfig() {
|
|
1841
1984
|
if (cachedConfig) return cachedConfig;
|
|
1842
|
-
const globalPath =
|
|
1843
|
-
const projectPath =
|
|
1985
|
+
const globalPath = path5.join(os2.homedir(), ".node9", "config.json");
|
|
1986
|
+
const projectPath = path5.join(process.cwd(), "node9.config.json");
|
|
1844
1987
|
const globalConfig = tryLoadConfig(globalPath);
|
|
1845
1988
|
const projectConfig = tryLoadConfig(projectPath);
|
|
1846
1989
|
const mergedSettings = {
|
|
@@ -1935,10 +2078,10 @@ function getConfig() {
|
|
|
1935
2078
|
return cachedConfig;
|
|
1936
2079
|
}
|
|
1937
2080
|
function tryLoadConfig(filePath) {
|
|
1938
|
-
if (!
|
|
2081
|
+
if (!fs3.existsSync(filePath)) return null;
|
|
1939
2082
|
let raw;
|
|
1940
2083
|
try {
|
|
1941
|
-
raw = JSON.parse(
|
|
2084
|
+
raw = JSON.parse(fs3.readFileSync(filePath, "utf-8"));
|
|
1942
2085
|
} catch (err) {
|
|
1943
2086
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1944
2087
|
process.stderr.write(
|
|
@@ -2000,9 +2143,9 @@ function getCredentials() {
|
|
|
2000
2143
|
};
|
|
2001
2144
|
}
|
|
2002
2145
|
try {
|
|
2003
|
-
const credPath =
|
|
2004
|
-
if (
|
|
2005
|
-
const creds = JSON.parse(
|
|
2146
|
+
const credPath = path5.join(os2.homedir(), ".node9", "credentials.json");
|
|
2147
|
+
if (fs3.existsSync(credPath)) {
|
|
2148
|
+
const creds = JSON.parse(fs3.readFileSync(credPath, "utf-8"));
|
|
2006
2149
|
const profileName = process.env.NODE9_PROFILE || "default";
|
|
2007
2150
|
const profile = creds[profileName];
|
|
2008
2151
|
if (profile?.apiKey) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@node9/proxy",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "The Sudo Command for AI Agents. Execution Security for Claude Code & MCP.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
"commander": "^14.0.3",
|
|
72
72
|
"execa": "^9.6.1",
|
|
73
73
|
"picomatch": "^4.0.3",
|
|
74
|
+
"safe-regex2": "^5.1.0",
|
|
74
75
|
"sh-syntax": "^0.5.8",
|
|
75
76
|
"zod": "^3.25.76"
|
|
76
77
|
},
|