@node9/proxy 1.0.19 → 1.1.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 +3 -1
- package/dist/cli.js +523 -251
- package/dist/cli.mjs +529 -257
- package/dist/index.js +178 -59
- package/dist/index.mjs +178 -59
- 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,10 +644,14 @@ 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" },
|
|
649
|
-
|
|
652
|
+
// Slack bot tokens: xoxb- + variable segment. Real tokens are ~50–80 chars;
|
|
653
|
+
// lower bound 20 avoids false negatives on partial tokens, upper 100 caps scan cost.
|
|
654
|
+
{ name: "Slack Bot Token", regex: /\bxoxb-[0-9A-Za-z-]{20,100}\b/, severity: "block" },
|
|
650
655
|
{ name: "OpenAI API Key", regex: /\bsk-[a-zA-Z0-9_-]{20,}\b/, severity: "block" },
|
|
651
656
|
{ name: "Stripe Secret Key", regex: /\bsk_(?:live|test)_[0-9a-zA-Z]{24}\b/, severity: "block" },
|
|
652
657
|
{
|
|
@@ -654,8 +659,76 @@ var DLP_PATTERNS = [
|
|
|
654
659
|
regex: /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/,
|
|
655
660
|
severity: "block"
|
|
656
661
|
},
|
|
662
|
+
// GCP service account JSON (detects the type field that uniquely identifies it)
|
|
663
|
+
{
|
|
664
|
+
name: "GCP Service Account",
|
|
665
|
+
regex: /"type"\s*:\s*"service_account"/,
|
|
666
|
+
severity: "block"
|
|
667
|
+
},
|
|
668
|
+
// NPM auth token in .npmrc format
|
|
669
|
+
{
|
|
670
|
+
name: "NPM Auth Token",
|
|
671
|
+
regex: /_authToken\s*=\s*[A-Za-z0-9_\-]{20,}/,
|
|
672
|
+
severity: "block"
|
|
673
|
+
},
|
|
657
674
|
{ name: "Bearer Token", regex: /Bearer\s+[a-zA-Z0-9\-._~+/]+=*/i, severity: "review" }
|
|
658
675
|
];
|
|
676
|
+
var SENSITIVE_PATH_PATTERNS = [
|
|
677
|
+
/[/\\]\.ssh[/\\]/i,
|
|
678
|
+
/[/\\]\.aws[/\\]/i,
|
|
679
|
+
/[/\\]\.config[/\\]gcloud[/\\]/i,
|
|
680
|
+
/[/\\]\.azure[/\\]/i,
|
|
681
|
+
/[/\\]\.kube[/\\]config$/i,
|
|
682
|
+
/[/\\]\.env($|\.)/i,
|
|
683
|
+
// .env, .env.local, .env.production — not .envoy
|
|
684
|
+
/[/\\]\.git-credentials$/i,
|
|
685
|
+
/[/\\]\.npmrc$/i,
|
|
686
|
+
/[/\\]\.docker[/\\]config\.json$/i,
|
|
687
|
+
/[/\\][^/\\]+\.pem$/i,
|
|
688
|
+
/[/\\][^/\\]+\.key$/i,
|
|
689
|
+
/[/\\][^/\\]+\.p12$/i,
|
|
690
|
+
/[/\\][^/\\]+\.pfx$/i,
|
|
691
|
+
/^\/etc\/passwd$/,
|
|
692
|
+
/^\/etc\/shadow$/,
|
|
693
|
+
/^\/etc\/sudoers$/,
|
|
694
|
+
/[/\\]credentials\.json$/i,
|
|
695
|
+
/[/\\]id_rsa$/i,
|
|
696
|
+
/[/\\]id_ed25519$/i,
|
|
697
|
+
/[/\\]id_ecdsa$/i
|
|
698
|
+
];
|
|
699
|
+
function scanFilePath(filePath, cwd = process.cwd()) {
|
|
700
|
+
if (!filePath) return null;
|
|
701
|
+
let resolved;
|
|
702
|
+
try {
|
|
703
|
+
const absolute = path4.resolve(cwd, filePath);
|
|
704
|
+
resolved = fs2.realpathSync.native(absolute);
|
|
705
|
+
} catch (err) {
|
|
706
|
+
const code = err.code;
|
|
707
|
+
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
708
|
+
resolved = path4.resolve(cwd, filePath);
|
|
709
|
+
} else {
|
|
710
|
+
return {
|
|
711
|
+
patternName: "Sensitive File Path",
|
|
712
|
+
fieldPath: "file_path",
|
|
713
|
+
redactedSample: filePath,
|
|
714
|
+
severity: "block"
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
const normalised = resolved.replace(/\\/g, "/");
|
|
719
|
+
for (const pattern of SENSITIVE_PATH_PATTERNS) {
|
|
720
|
+
if (pattern.test(normalised)) {
|
|
721
|
+
return {
|
|
722
|
+
patternName: "Sensitive File Path",
|
|
723
|
+
fieldPath: "file_path",
|
|
724
|
+
redactedSample: filePath,
|
|
725
|
+
// show original path in alert, not resolved
|
|
726
|
+
severity: "block"
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
return null;
|
|
731
|
+
}
|
|
659
732
|
function maskSecret(raw, pattern) {
|
|
660
733
|
const match = raw.match(pattern);
|
|
661
734
|
if (!match) return "****";
|
|
@@ -713,17 +786,17 @@ function scanArgs(args, depth = 0, fieldPath = "args") {
|
|
|
713
786
|
}
|
|
714
787
|
|
|
715
788
|
// src/core.ts
|
|
716
|
-
var PAUSED_FILE =
|
|
717
|
-
var TRUST_FILE =
|
|
718
|
-
var LOCAL_AUDIT_LOG =
|
|
719
|
-
var HOOK_DEBUG_LOG =
|
|
789
|
+
var PAUSED_FILE = path5.join(os2.homedir(), ".node9", "PAUSED");
|
|
790
|
+
var TRUST_FILE = path5.join(os2.homedir(), ".node9", "trust.json");
|
|
791
|
+
var LOCAL_AUDIT_LOG = path5.join(os2.homedir(), ".node9", "audit.log");
|
|
792
|
+
var HOOK_DEBUG_LOG = path5.join(os2.homedir(), ".node9", "hook-debug.log");
|
|
720
793
|
function checkPause() {
|
|
721
794
|
try {
|
|
722
|
-
if (!
|
|
723
|
-
const state = JSON.parse(
|
|
795
|
+
if (!fs3.existsSync(PAUSED_FILE)) return { paused: false };
|
|
796
|
+
const state = JSON.parse(fs3.readFileSync(PAUSED_FILE, "utf-8"));
|
|
724
797
|
if (state.expiry > 0 && Date.now() >= state.expiry) {
|
|
725
798
|
try {
|
|
726
|
-
|
|
799
|
+
fs3.unlinkSync(PAUSED_FILE);
|
|
727
800
|
} catch {
|
|
728
801
|
}
|
|
729
802
|
return { paused: false };
|
|
@@ -734,20 +807,66 @@ function checkPause() {
|
|
|
734
807
|
}
|
|
735
808
|
}
|
|
736
809
|
function atomicWriteSync(filePath, data, options) {
|
|
737
|
-
const dir =
|
|
738
|
-
if (!
|
|
810
|
+
const dir = path5.dirname(filePath);
|
|
811
|
+
if (!fs3.existsSync(dir)) fs3.mkdirSync(dir, { recursive: true });
|
|
739
812
|
const tmpPath = `${filePath}.${os2.hostname()}.${process.pid}.tmp`;
|
|
740
|
-
|
|
741
|
-
|
|
813
|
+
fs3.writeFileSync(tmpPath, data, options);
|
|
814
|
+
fs3.renameSync(tmpPath, filePath);
|
|
815
|
+
}
|
|
816
|
+
var MAX_REGEX_LENGTH = 100;
|
|
817
|
+
var REGEX_CACHE_MAX = 500;
|
|
818
|
+
var regexCache = /* @__PURE__ */ new Map();
|
|
819
|
+
function validateRegex(pattern) {
|
|
820
|
+
if (!pattern) return "Pattern is required";
|
|
821
|
+
if (pattern.length > MAX_REGEX_LENGTH) return `Pattern exceeds max length of ${MAX_REGEX_LENGTH}`;
|
|
822
|
+
try {
|
|
823
|
+
new RegExp(pattern);
|
|
824
|
+
} catch (e) {
|
|
825
|
+
return `Invalid regex syntax: ${e.message}`;
|
|
826
|
+
}
|
|
827
|
+
if (/\\\d+[*+{]/.test(pattern)) return "Quantified backreferences are forbidden (ReDoS risk)";
|
|
828
|
+
if (!safeRegex(pattern)) return "Pattern rejected: potential ReDoS vulnerability detected";
|
|
829
|
+
return null;
|
|
830
|
+
}
|
|
831
|
+
function getCompiledRegex(pattern, flags = "") {
|
|
832
|
+
if (flags && !/^[gimsuy]+$/.test(flags)) {
|
|
833
|
+
if (process.env.NODE9_DEBUG === "1") console.error(`[Node9] Invalid regex flags: "${flags}"`);
|
|
834
|
+
return null;
|
|
835
|
+
}
|
|
836
|
+
const key = `${pattern}\0${flags}`;
|
|
837
|
+
if (regexCache.has(key)) {
|
|
838
|
+
const cached = regexCache.get(key);
|
|
839
|
+
regexCache.delete(key);
|
|
840
|
+
regexCache.set(key, cached);
|
|
841
|
+
return cached;
|
|
842
|
+
}
|
|
843
|
+
const err = validateRegex(pattern);
|
|
844
|
+
if (err) {
|
|
845
|
+
if (process.env.NODE9_DEBUG === "1")
|
|
846
|
+
console.error(`[Node9] Regex blocked: ${err} \u2014 pattern: "${pattern}"`);
|
|
847
|
+
return null;
|
|
848
|
+
}
|
|
849
|
+
try {
|
|
850
|
+
const re = new RegExp(pattern, flags);
|
|
851
|
+
if (regexCache.size >= REGEX_CACHE_MAX) {
|
|
852
|
+
const oldest = regexCache.keys().next().value;
|
|
853
|
+
if (oldest) regexCache.delete(oldest);
|
|
854
|
+
}
|
|
855
|
+
regexCache.set(key, re);
|
|
856
|
+
return re;
|
|
857
|
+
} catch (e) {
|
|
858
|
+
if (process.env.NODE9_DEBUG === "1") console.error(`[Node9] Regex compile failed:`, e);
|
|
859
|
+
return null;
|
|
860
|
+
}
|
|
742
861
|
}
|
|
743
862
|
function getActiveTrustSession(toolName) {
|
|
744
863
|
try {
|
|
745
|
-
if (!
|
|
746
|
-
const trust = JSON.parse(
|
|
864
|
+
if (!fs3.existsSync(TRUST_FILE)) return false;
|
|
865
|
+
const trust = JSON.parse(fs3.readFileSync(TRUST_FILE, "utf-8"));
|
|
747
866
|
const now = Date.now();
|
|
748
867
|
const active = trust.entries.filter((e) => e.expiry > now);
|
|
749
868
|
if (active.length !== trust.entries.length) {
|
|
750
|
-
|
|
869
|
+
fs3.writeFileSync(TRUST_FILE, JSON.stringify({ entries: active }, null, 2));
|
|
751
870
|
}
|
|
752
871
|
return active.some((e) => e.tool === toolName || matchesPattern(toolName, e.tool));
|
|
753
872
|
} catch {
|
|
@@ -758,8 +877,8 @@ function writeTrustSession(toolName, durationMs) {
|
|
|
758
877
|
try {
|
|
759
878
|
let trust = { entries: [] };
|
|
760
879
|
try {
|
|
761
|
-
if (
|
|
762
|
-
trust = JSON.parse(
|
|
880
|
+
if (fs3.existsSync(TRUST_FILE)) {
|
|
881
|
+
trust = JSON.parse(fs3.readFileSync(TRUST_FILE, "utf-8"));
|
|
763
882
|
}
|
|
764
883
|
} catch {
|
|
765
884
|
}
|
|
@@ -775,9 +894,9 @@ function writeTrustSession(toolName, durationMs) {
|
|
|
775
894
|
}
|
|
776
895
|
function appendToLog(logPath, entry) {
|
|
777
896
|
try {
|
|
778
|
-
const dir =
|
|
779
|
-
if (!
|
|
780
|
-
|
|
897
|
+
const dir = path5.dirname(logPath);
|
|
898
|
+
if (!fs3.existsSync(dir)) fs3.mkdirSync(dir, { recursive: true });
|
|
899
|
+
fs3.appendFileSync(logPath, JSON.stringify(entry) + "\n");
|
|
781
900
|
} catch {
|
|
782
901
|
}
|
|
783
902
|
}
|
|
@@ -819,9 +938,9 @@ function matchesPattern(text, patterns) {
|
|
|
819
938
|
const withoutDotSlash = text.replace(/^\.\//, "");
|
|
820
939
|
return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
|
|
821
940
|
}
|
|
822
|
-
function getNestedValue(obj,
|
|
941
|
+
function getNestedValue(obj, path6) {
|
|
823
942
|
if (!obj || typeof obj !== "object") return null;
|
|
824
|
-
return
|
|
943
|
+
return path6.split(".").reduce((prev, curr) => prev?.[curr], obj);
|
|
825
944
|
}
|
|
826
945
|
function evaluateSmartConditions(args, rule) {
|
|
827
946
|
if (!rule.conditions || rule.conditions.length === 0) return true;
|
|
@@ -840,19 +959,16 @@ function evaluateSmartConditions(args, rule) {
|
|
|
840
959
|
return val !== null && cond.value ? !val.includes(cond.value) : true;
|
|
841
960
|
case "matches": {
|
|
842
961
|
if (val === null || !cond.value) return false;
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
return false;
|
|
847
|
-
}
|
|
962
|
+
const reM = getCompiledRegex(cond.value, cond.flags ?? "");
|
|
963
|
+
if (!reM) return false;
|
|
964
|
+
return reM.test(val);
|
|
848
965
|
}
|
|
849
966
|
case "notMatches": {
|
|
850
|
-
if (
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
}
|
|
967
|
+
if (!cond.value) return false;
|
|
968
|
+
if (val === null) return true;
|
|
969
|
+
const reN = getCompiledRegex(cond.value, cond.flags ?? "");
|
|
970
|
+
if (!reN) return false;
|
|
971
|
+
return !reN.test(val);
|
|
856
972
|
}
|
|
857
973
|
case "matchesGlob":
|
|
858
974
|
return val !== null && cond.value ? pm.isMatch(val, cond.value) : false;
|
|
@@ -1174,9 +1290,9 @@ var ADVISORY_SMART_RULES = [
|
|
|
1174
1290
|
var cachedConfig = null;
|
|
1175
1291
|
function getInternalToken() {
|
|
1176
1292
|
try {
|
|
1177
|
-
const pidFile =
|
|
1178
|
-
if (!
|
|
1179
|
-
const data = JSON.parse(
|
|
1293
|
+
const pidFile = path5.join(os2.homedir(), ".node9", "daemon.pid");
|
|
1294
|
+
if (!fs3.existsSync(pidFile)) return null;
|
|
1295
|
+
const data = JSON.parse(fs3.readFileSync(pidFile, "utf-8"));
|
|
1180
1296
|
process.kill(data.pid, 0);
|
|
1181
1297
|
return data.internalToken ?? null;
|
|
1182
1298
|
} catch {
|
|
@@ -1296,10 +1412,10 @@ function isIgnoredTool(toolName) {
|
|
|
1296
1412
|
var DAEMON_PORT = 7391;
|
|
1297
1413
|
var DAEMON_HOST = "127.0.0.1";
|
|
1298
1414
|
function isDaemonRunning() {
|
|
1299
|
-
const pidFile =
|
|
1300
|
-
if (
|
|
1415
|
+
const pidFile = path5.join(os2.homedir(), ".node9", "daemon.pid");
|
|
1416
|
+
if (fs3.existsSync(pidFile)) {
|
|
1301
1417
|
try {
|
|
1302
|
-
const { pid, port } = JSON.parse(
|
|
1418
|
+
const { pid, port } = JSON.parse(fs3.readFileSync(pidFile, "utf-8"));
|
|
1303
1419
|
if (port !== DAEMON_PORT) return false;
|
|
1304
1420
|
process.kill(pid, 0);
|
|
1305
1421
|
return true;
|
|
@@ -1319,9 +1435,9 @@ function isDaemonRunning() {
|
|
|
1319
1435
|
}
|
|
1320
1436
|
function getPersistentDecision(toolName) {
|
|
1321
1437
|
try {
|
|
1322
|
-
const file =
|
|
1323
|
-
if (!
|
|
1324
|
-
const decisions = JSON.parse(
|
|
1438
|
+
const file = path5.join(os2.homedir(), ".node9", "decisions.json");
|
|
1439
|
+
if (!fs3.existsSync(file)) return null;
|
|
1440
|
+
const decisions = JSON.parse(fs3.readFileSync(file, "utf-8"));
|
|
1325
1441
|
const d = decisions[toolName];
|
|
1326
1442
|
if (d === "allow" || d === "deny") return d;
|
|
1327
1443
|
} catch {
|
|
@@ -1403,7 +1519,7 @@ async function resolveViaDaemon(id, decision, internalToken) {
|
|
|
1403
1519
|
signal: AbortSignal.timeout(3e3)
|
|
1404
1520
|
});
|
|
1405
1521
|
}
|
|
1406
|
-
var ACTIVITY_SOCKET_PATH = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" :
|
|
1522
|
+
var ACTIVITY_SOCKET_PATH = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path5.join(os2.tmpdir(), "node9-activity.sock");
|
|
1407
1523
|
function notifyActivity(data) {
|
|
1408
1524
|
return new Promise((resolve) => {
|
|
1409
1525
|
try {
|
|
@@ -1465,7 +1581,9 @@ async function _authorizeHeadlessCore(toolName, args, allowTerminalFallback = fa
|
|
|
1465
1581
|
let policyMatchedWord;
|
|
1466
1582
|
let riskMetadata;
|
|
1467
1583
|
if (config.policy.dlp.enabled && (!isIgnoredTool(toolName) || config.policy.dlp.scanIgnoredTools)) {
|
|
1468
|
-
const
|
|
1584
|
+
const argsObj = args && typeof args === "object" && !Array.isArray(args) ? args : {};
|
|
1585
|
+
const filePath = String(argsObj.file_path ?? argsObj.path ?? argsObj.filename ?? "");
|
|
1586
|
+
const dlpMatch = (filePath ? scanFilePath(filePath) : null) ?? scanArgs(args);
|
|
1469
1587
|
if (dlpMatch) {
|
|
1470
1588
|
const dlpReason = `\u{1F6A8} DATA LOSS PREVENTION: ${dlpMatch.patternName} detected in field "${dlpMatch.fieldPath}" (${dlpMatch.redactedSample})`;
|
|
1471
1589
|
if (dlpMatch.severity === "block") {
|
|
@@ -1837,10 +1955,10 @@ REASON: Action blocked because no approval channels are available. (Native/Brows
|
|
|
1837
1955
|
}
|
|
1838
1956
|
return finalResult;
|
|
1839
1957
|
}
|
|
1840
|
-
function getConfig() {
|
|
1841
|
-
if (cachedConfig) return cachedConfig;
|
|
1842
|
-
const globalPath =
|
|
1843
|
-
const projectPath =
|
|
1958
|
+
function getConfig(cwd) {
|
|
1959
|
+
if (!cwd && cachedConfig) return cachedConfig;
|
|
1960
|
+
const globalPath = path5.join(os2.homedir(), ".node9", "config.json");
|
|
1961
|
+
const projectPath = path5.join(cwd ?? process.cwd(), "node9.config.json");
|
|
1844
1962
|
const globalConfig = tryLoadConfig(globalPath);
|
|
1845
1963
|
const projectConfig = tryLoadConfig(projectPath);
|
|
1846
1964
|
const mergedSettings = {
|
|
@@ -1927,18 +2045,19 @@ function getConfig() {
|
|
|
1927
2045
|
mergedPolicy.snapshot.tools = [...new Set(mergedPolicy.snapshot.tools)];
|
|
1928
2046
|
mergedPolicy.snapshot.onlyPaths = [...new Set(mergedPolicy.snapshot.onlyPaths)];
|
|
1929
2047
|
mergedPolicy.snapshot.ignorePaths = [...new Set(mergedPolicy.snapshot.ignorePaths)];
|
|
1930
|
-
|
|
2048
|
+
const result = {
|
|
1931
2049
|
settings: mergedSettings,
|
|
1932
2050
|
policy: mergedPolicy,
|
|
1933
2051
|
environments: mergedEnvironments
|
|
1934
2052
|
};
|
|
1935
|
-
|
|
2053
|
+
if (!cwd) cachedConfig = result;
|
|
2054
|
+
return result;
|
|
1936
2055
|
}
|
|
1937
2056
|
function tryLoadConfig(filePath) {
|
|
1938
|
-
if (!
|
|
2057
|
+
if (!fs3.existsSync(filePath)) return null;
|
|
1939
2058
|
let raw;
|
|
1940
2059
|
try {
|
|
1941
|
-
raw = JSON.parse(
|
|
2060
|
+
raw = JSON.parse(fs3.readFileSync(filePath, "utf-8"));
|
|
1942
2061
|
} catch (err) {
|
|
1943
2062
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1944
2063
|
process.stderr.write(
|
|
@@ -2000,9 +2119,9 @@ function getCredentials() {
|
|
|
2000
2119
|
};
|
|
2001
2120
|
}
|
|
2002
2121
|
try {
|
|
2003
|
-
const credPath =
|
|
2004
|
-
if (
|
|
2005
|
-
const creds = JSON.parse(
|
|
2122
|
+
const credPath = path5.join(os2.homedir(), ".node9", "credentials.json");
|
|
2123
|
+
if (fs3.existsSync(credPath)) {
|
|
2124
|
+
const creds = JSON.parse(fs3.readFileSync(credPath, "utf-8"));
|
|
2006
2125
|
const profileName = process.env.NODE9_PROFILE || "default";
|
|
2007
2126
|
const profile = creds[profileName];
|
|
2008
2127
|
if (profile?.apiKey) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@node9/proxy",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
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
|
},
|