@node9/proxy 1.0.18 → 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 +6 -1
- package/dist/cli.js +528 -238
- package/dist/cli.mjs +534 -244
- package/dist/index.js +207 -57
- package/dist/index.mjs +207 -57
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -37,13 +37,14 @@ module.exports = __toCommonJS(src_exports);
|
|
|
37
37
|
// src/core.ts
|
|
38
38
|
var import_chalk2 = __toESM(require("chalk"));
|
|
39
39
|
var import_prompts = require("@inquirer/prompts");
|
|
40
|
-
var
|
|
41
|
-
var
|
|
40
|
+
var import_fs3 = __toESM(require("fs"));
|
|
41
|
+
var import_path5 = __toESM(require("path"));
|
|
42
42
|
var import_os2 = __toESM(require("os"));
|
|
43
43
|
var import_net = __toESM(require("net"));
|
|
44
44
|
var import_crypto = require("crypto");
|
|
45
45
|
var import_child_process2 = require("child_process");
|
|
46
46
|
var import_picomatch = __toESM(require("picomatch"));
|
|
47
|
+
var import_safe_regex2 = __toESM(require("safe-regex2"));
|
|
47
48
|
var import_sh_syntax = require("sh-syntax");
|
|
48
49
|
|
|
49
50
|
// src/ui/native.ts
|
|
@@ -465,8 +466,8 @@ function sanitizeConfig(raw) {
|
|
|
465
466
|
}
|
|
466
467
|
}
|
|
467
468
|
const lines = result.error.issues.map((issue) => {
|
|
468
|
-
const
|
|
469
|
-
return ` \u2022 ${
|
|
469
|
+
const path6 = issue.path.length > 0 ? issue.path.join(".") : "root";
|
|
470
|
+
return ` \u2022 ${path6}: ${issue.message}`;
|
|
470
471
|
});
|
|
471
472
|
return {
|
|
472
473
|
sanitized,
|
|
@@ -679,6 +680,8 @@ function readActiveShields() {
|
|
|
679
680
|
}
|
|
680
681
|
|
|
681
682
|
// src/dlp.ts
|
|
683
|
+
var import_fs2 = __toESM(require("fs"));
|
|
684
|
+
var import_path4 = __toESM(require("path"));
|
|
682
685
|
var DLP_PATTERNS = [
|
|
683
686
|
{ name: "AWS Access Key ID", regex: /\bAKIA[0-9A-Z]{16}\b/, severity: "block" },
|
|
684
687
|
{ name: "GitHub Token", regex: /\bgh[pous]_[A-Za-z0-9]{36}\b/, severity: "block" },
|
|
@@ -690,8 +693,76 @@ var DLP_PATTERNS = [
|
|
|
690
693
|
regex: /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/,
|
|
691
694
|
severity: "block"
|
|
692
695
|
},
|
|
696
|
+
// GCP service account JSON (detects the type field that uniquely identifies it)
|
|
697
|
+
{
|
|
698
|
+
name: "GCP Service Account",
|
|
699
|
+
regex: /"type"\s*:\s*"service_account"/,
|
|
700
|
+
severity: "block"
|
|
701
|
+
},
|
|
702
|
+
// NPM auth token in .npmrc format
|
|
703
|
+
{
|
|
704
|
+
name: "NPM Auth Token",
|
|
705
|
+
regex: /_authToken\s*=\s*[A-Za-z0-9_\-]{20,}/,
|
|
706
|
+
severity: "block"
|
|
707
|
+
},
|
|
693
708
|
{ name: "Bearer Token", regex: /Bearer\s+[a-zA-Z0-9\-._~+/]+=*/i, severity: "review" }
|
|
694
709
|
];
|
|
710
|
+
var SENSITIVE_PATH_PATTERNS = [
|
|
711
|
+
/[/\\]\.ssh[/\\]/i,
|
|
712
|
+
/[/\\]\.aws[/\\]/i,
|
|
713
|
+
/[/\\]\.config[/\\]gcloud[/\\]/i,
|
|
714
|
+
/[/\\]\.azure[/\\]/i,
|
|
715
|
+
/[/\\]\.kube[/\\]config$/i,
|
|
716
|
+
/[/\\]\.env($|\.)/i,
|
|
717
|
+
// .env, .env.local, .env.production — not .envoy
|
|
718
|
+
/[/\\]\.git-credentials$/i,
|
|
719
|
+
/[/\\]\.npmrc$/i,
|
|
720
|
+
/[/\\]\.docker[/\\]config\.json$/i,
|
|
721
|
+
/[/\\][^/\\]+\.pem$/i,
|
|
722
|
+
/[/\\][^/\\]+\.key$/i,
|
|
723
|
+
/[/\\][^/\\]+\.p12$/i,
|
|
724
|
+
/[/\\][^/\\]+\.pfx$/i,
|
|
725
|
+
/^\/etc\/passwd$/,
|
|
726
|
+
/^\/etc\/shadow$/,
|
|
727
|
+
/^\/etc\/sudoers$/,
|
|
728
|
+
/[/\\]credentials\.json$/i,
|
|
729
|
+
/[/\\]id_rsa$/i,
|
|
730
|
+
/[/\\]id_ed25519$/i,
|
|
731
|
+
/[/\\]id_ecdsa$/i
|
|
732
|
+
];
|
|
733
|
+
function scanFilePath(filePath, cwd = process.cwd()) {
|
|
734
|
+
if (!filePath) return null;
|
|
735
|
+
let resolved;
|
|
736
|
+
try {
|
|
737
|
+
const absolute = import_path4.default.resolve(cwd, filePath);
|
|
738
|
+
resolved = import_fs2.default.realpathSync.native(absolute);
|
|
739
|
+
} catch (err) {
|
|
740
|
+
const code = err.code;
|
|
741
|
+
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
742
|
+
resolved = import_path4.default.resolve(cwd, filePath);
|
|
743
|
+
} else {
|
|
744
|
+
return {
|
|
745
|
+
patternName: "Sensitive File Path",
|
|
746
|
+
fieldPath: "file_path",
|
|
747
|
+
redactedSample: filePath,
|
|
748
|
+
severity: "block"
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
const normalised = resolved.replace(/\\/g, "/");
|
|
753
|
+
for (const pattern of SENSITIVE_PATH_PATTERNS) {
|
|
754
|
+
if (pattern.test(normalised)) {
|
|
755
|
+
return {
|
|
756
|
+
patternName: "Sensitive File Path",
|
|
757
|
+
fieldPath: "file_path",
|
|
758
|
+
redactedSample: filePath,
|
|
759
|
+
// show original path in alert, not resolved
|
|
760
|
+
severity: "block"
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
return null;
|
|
765
|
+
}
|
|
695
766
|
function maskSecret(raw, pattern) {
|
|
696
767
|
const match = raw.match(pattern);
|
|
697
768
|
if (!match) return "****";
|
|
@@ -749,17 +820,17 @@ function scanArgs(args, depth = 0, fieldPath = "args") {
|
|
|
749
820
|
}
|
|
750
821
|
|
|
751
822
|
// src/core.ts
|
|
752
|
-
var PAUSED_FILE =
|
|
753
|
-
var TRUST_FILE =
|
|
754
|
-
var LOCAL_AUDIT_LOG =
|
|
755
|
-
var HOOK_DEBUG_LOG =
|
|
823
|
+
var PAUSED_FILE = import_path5.default.join(import_os2.default.homedir(), ".node9", "PAUSED");
|
|
824
|
+
var TRUST_FILE = import_path5.default.join(import_os2.default.homedir(), ".node9", "trust.json");
|
|
825
|
+
var LOCAL_AUDIT_LOG = import_path5.default.join(import_os2.default.homedir(), ".node9", "audit.log");
|
|
826
|
+
var HOOK_DEBUG_LOG = import_path5.default.join(import_os2.default.homedir(), ".node9", "hook-debug.log");
|
|
756
827
|
function checkPause() {
|
|
757
828
|
try {
|
|
758
|
-
if (!
|
|
759
|
-
const state = JSON.parse(
|
|
829
|
+
if (!import_fs3.default.existsSync(PAUSED_FILE)) return { paused: false };
|
|
830
|
+
const state = JSON.parse(import_fs3.default.readFileSync(PAUSED_FILE, "utf-8"));
|
|
760
831
|
if (state.expiry > 0 && Date.now() >= state.expiry) {
|
|
761
832
|
try {
|
|
762
|
-
|
|
833
|
+
import_fs3.default.unlinkSync(PAUSED_FILE);
|
|
763
834
|
} catch {
|
|
764
835
|
}
|
|
765
836
|
return { paused: false };
|
|
@@ -770,20 +841,93 @@ function checkPause() {
|
|
|
770
841
|
}
|
|
771
842
|
}
|
|
772
843
|
function atomicWriteSync(filePath, data, options) {
|
|
773
|
-
const dir =
|
|
774
|
-
if (!
|
|
844
|
+
const dir = import_path5.default.dirname(filePath);
|
|
845
|
+
if (!import_fs3.default.existsSync(dir)) import_fs3.default.mkdirSync(dir, { recursive: true });
|
|
775
846
|
const tmpPath = `${filePath}.${import_os2.default.hostname()}.${process.pid}.tmp`;
|
|
776
|
-
|
|
777
|
-
|
|
847
|
+
import_fs3.default.writeFileSync(tmpPath, data, options);
|
|
848
|
+
import_fs3.default.renameSync(tmpPath, filePath);
|
|
849
|
+
}
|
|
850
|
+
var MAX_REGEX_LENGTH = 100;
|
|
851
|
+
var REGEX_CACHE_MAX = 500;
|
|
852
|
+
var regexCache = /* @__PURE__ */ new Map();
|
|
853
|
+
function validateRegex(pattern) {
|
|
854
|
+
if (!pattern) return "Pattern is required";
|
|
855
|
+
if (pattern.length > MAX_REGEX_LENGTH) return `Pattern exceeds max length of ${MAX_REGEX_LENGTH}`;
|
|
856
|
+
let parens = 0, brackets = 0, isEscaped = false, inCharClass = false;
|
|
857
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
858
|
+
const char = pattern[i];
|
|
859
|
+
if (isEscaped) {
|
|
860
|
+
isEscaped = false;
|
|
861
|
+
continue;
|
|
862
|
+
}
|
|
863
|
+
if (char === "\\") {
|
|
864
|
+
isEscaped = true;
|
|
865
|
+
continue;
|
|
866
|
+
}
|
|
867
|
+
if (char === "[" && !inCharClass) {
|
|
868
|
+
inCharClass = true;
|
|
869
|
+
brackets++;
|
|
870
|
+
continue;
|
|
871
|
+
}
|
|
872
|
+
if (char === "]" && inCharClass) {
|
|
873
|
+
inCharClass = false;
|
|
874
|
+
brackets--;
|
|
875
|
+
continue;
|
|
876
|
+
}
|
|
877
|
+
if (inCharClass) continue;
|
|
878
|
+
if (char === "(") parens++;
|
|
879
|
+
else if (char === ")") parens--;
|
|
880
|
+
}
|
|
881
|
+
if (parens !== 0) return "Unbalanced parentheses";
|
|
882
|
+
if (brackets !== 0) return "Unbalanced brackets";
|
|
883
|
+
if (/\\\d+[*+{]/.test(pattern)) return "Quantified backreferences are forbidden (ReDoS risk)";
|
|
884
|
+
if (!(0, import_safe_regex2.default)(pattern)) return "Pattern rejected: potential ReDoS vulnerability detected";
|
|
885
|
+
try {
|
|
886
|
+
new RegExp(pattern);
|
|
887
|
+
} catch (e) {
|
|
888
|
+
return `Invalid regex syntax: ${e.message}`;
|
|
889
|
+
}
|
|
890
|
+
return null;
|
|
891
|
+
}
|
|
892
|
+
function getCompiledRegex(pattern, flags = "") {
|
|
893
|
+
if (flags && !/^[gimsuy]+$/.test(flags)) {
|
|
894
|
+
if (process.env.NODE9_DEBUG === "1") console.error(`[Node9] Invalid regex flags: "${flags}"`);
|
|
895
|
+
return null;
|
|
896
|
+
}
|
|
897
|
+
const key = `${pattern}\0${flags}`;
|
|
898
|
+
if (regexCache.has(key)) {
|
|
899
|
+
const cached = regexCache.get(key);
|
|
900
|
+
regexCache.delete(key);
|
|
901
|
+
regexCache.set(key, cached);
|
|
902
|
+
return cached;
|
|
903
|
+
}
|
|
904
|
+
const err = validateRegex(pattern);
|
|
905
|
+
if (err) {
|
|
906
|
+
if (process.env.NODE9_DEBUG === "1")
|
|
907
|
+
console.error(`[Node9] Regex blocked: ${err} \u2014 pattern: "${pattern}"`);
|
|
908
|
+
return null;
|
|
909
|
+
}
|
|
910
|
+
try {
|
|
911
|
+
const re = new RegExp(pattern, flags);
|
|
912
|
+
if (regexCache.size >= REGEX_CACHE_MAX) {
|
|
913
|
+
const oldest = regexCache.keys().next().value;
|
|
914
|
+
if (oldest) regexCache.delete(oldest);
|
|
915
|
+
}
|
|
916
|
+
regexCache.set(key, re);
|
|
917
|
+
return re;
|
|
918
|
+
} catch (e) {
|
|
919
|
+
if (process.env.NODE9_DEBUG === "1") console.error(`[Node9] Regex compile failed:`, e);
|
|
920
|
+
return null;
|
|
921
|
+
}
|
|
778
922
|
}
|
|
779
923
|
function getActiveTrustSession(toolName) {
|
|
780
924
|
try {
|
|
781
|
-
if (!
|
|
782
|
-
const trust = JSON.parse(
|
|
925
|
+
if (!import_fs3.default.existsSync(TRUST_FILE)) return false;
|
|
926
|
+
const trust = JSON.parse(import_fs3.default.readFileSync(TRUST_FILE, "utf-8"));
|
|
783
927
|
const now = Date.now();
|
|
784
928
|
const active = trust.entries.filter((e) => e.expiry > now);
|
|
785
929
|
if (active.length !== trust.entries.length) {
|
|
786
|
-
|
|
930
|
+
import_fs3.default.writeFileSync(TRUST_FILE, JSON.stringify({ entries: active }, null, 2));
|
|
787
931
|
}
|
|
788
932
|
return active.some((e) => e.tool === toolName || matchesPattern(toolName, e.tool));
|
|
789
933
|
} catch {
|
|
@@ -794,8 +938,8 @@ function writeTrustSession(toolName, durationMs) {
|
|
|
794
938
|
try {
|
|
795
939
|
let trust = { entries: [] };
|
|
796
940
|
try {
|
|
797
|
-
if (
|
|
798
|
-
trust = JSON.parse(
|
|
941
|
+
if (import_fs3.default.existsSync(TRUST_FILE)) {
|
|
942
|
+
trust = JSON.parse(import_fs3.default.readFileSync(TRUST_FILE, "utf-8"));
|
|
799
943
|
}
|
|
800
944
|
} catch {
|
|
801
945
|
}
|
|
@@ -811,9 +955,9 @@ function writeTrustSession(toolName, durationMs) {
|
|
|
811
955
|
}
|
|
812
956
|
function appendToLog(logPath, entry) {
|
|
813
957
|
try {
|
|
814
|
-
const dir =
|
|
815
|
-
if (!
|
|
816
|
-
|
|
958
|
+
const dir = import_path5.default.dirname(logPath);
|
|
959
|
+
if (!import_fs3.default.existsSync(dir)) import_fs3.default.mkdirSync(dir, { recursive: true });
|
|
960
|
+
import_fs3.default.appendFileSync(logPath, JSON.stringify(entry) + "\n");
|
|
817
961
|
} catch {
|
|
818
962
|
}
|
|
819
963
|
}
|
|
@@ -855,9 +999,9 @@ function matchesPattern(text, patterns) {
|
|
|
855
999
|
const withoutDotSlash = text.replace(/^\.\//, "");
|
|
856
1000
|
return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
|
|
857
1001
|
}
|
|
858
|
-
function getNestedValue(obj,
|
|
1002
|
+
function getNestedValue(obj, path6) {
|
|
859
1003
|
if (!obj || typeof obj !== "object") return null;
|
|
860
|
-
return
|
|
1004
|
+
return path6.split(".").reduce((prev, curr) => prev?.[curr], obj);
|
|
861
1005
|
}
|
|
862
1006
|
function evaluateSmartConditions(args, rule) {
|
|
863
1007
|
if (!rule.conditions || rule.conditions.length === 0) return true;
|
|
@@ -876,19 +1020,16 @@ function evaluateSmartConditions(args, rule) {
|
|
|
876
1020
|
return val !== null && cond.value ? !val.includes(cond.value) : true;
|
|
877
1021
|
case "matches": {
|
|
878
1022
|
if (val === null || !cond.value) return false;
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
return false;
|
|
883
|
-
}
|
|
1023
|
+
const reM = getCompiledRegex(cond.value, cond.flags ?? "");
|
|
1024
|
+
if (!reM) return false;
|
|
1025
|
+
return reM.test(val);
|
|
884
1026
|
}
|
|
885
1027
|
case "notMatches": {
|
|
886
|
-
if (
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
}
|
|
1028
|
+
if (!cond.value) return false;
|
|
1029
|
+
if (val === null) return true;
|
|
1030
|
+
const reN = getCompiledRegex(cond.value, cond.flags ?? "");
|
|
1031
|
+
if (!reN) return false;
|
|
1032
|
+
return !reN.test(val);
|
|
892
1033
|
}
|
|
893
1034
|
case "matchesGlob":
|
|
894
1035
|
return val !== null && cond.value ? import_picomatch.default.isMatch(val, cond.value) : false;
|
|
@@ -1115,7 +1256,7 @@ var DEFAULT_CONFIG = {
|
|
|
1115
1256
|
{
|
|
1116
1257
|
field: "command",
|
|
1117
1258
|
op: "matches",
|
|
1118
|
-
value: "git
|
|
1259
|
+
value: "^\\s*git\\b.*\\bpush\\b.*(--force|--force-with-lease|-f\\b)",
|
|
1119
1260
|
flags: "i"
|
|
1120
1261
|
}
|
|
1121
1262
|
],
|
|
@@ -1126,7 +1267,14 @@ var DEFAULT_CONFIG = {
|
|
|
1126
1267
|
{
|
|
1127
1268
|
name: "review-git-push",
|
|
1128
1269
|
tool: "bash",
|
|
1129
|
-
conditions: [
|
|
1270
|
+
conditions: [
|
|
1271
|
+
{
|
|
1272
|
+
field: "command",
|
|
1273
|
+
op: "matches",
|
|
1274
|
+
value: "^\\s*git\\b.*\\bpush\\b(?!.*(-f\\b|--force|--force-with-lease))",
|
|
1275
|
+
flags: "i"
|
|
1276
|
+
}
|
|
1277
|
+
],
|
|
1130
1278
|
conditionMode: "all",
|
|
1131
1279
|
verdict: "review",
|
|
1132
1280
|
reason: "git push sends changes to a shared remote"
|
|
@@ -1138,7 +1286,7 @@ var DEFAULT_CONFIG = {
|
|
|
1138
1286
|
{
|
|
1139
1287
|
field: "command",
|
|
1140
1288
|
op: "matches",
|
|
1141
|
-
value: "git\\
|
|
1289
|
+
value: "^\\s*git\\b.*(reset\\s+--hard|clean\\s+-[fdxX]|\\brebase\\b|tag\\s+-d|branch\\s+-[dD])",
|
|
1142
1290
|
flags: "i"
|
|
1143
1291
|
}
|
|
1144
1292
|
],
|
|
@@ -1203,9 +1351,9 @@ var ADVISORY_SMART_RULES = [
|
|
|
1203
1351
|
var cachedConfig = null;
|
|
1204
1352
|
function getInternalToken() {
|
|
1205
1353
|
try {
|
|
1206
|
-
const pidFile =
|
|
1207
|
-
if (!
|
|
1208
|
-
const data = JSON.parse(
|
|
1354
|
+
const pidFile = import_path5.default.join(import_os2.default.homedir(), ".node9", "daemon.pid");
|
|
1355
|
+
if (!import_fs3.default.existsSync(pidFile)) return null;
|
|
1356
|
+
const data = JSON.parse(import_fs3.default.readFileSync(pidFile, "utf-8"));
|
|
1209
1357
|
process.kill(data.pid, 0);
|
|
1210
1358
|
return data.internalToken ?? null;
|
|
1211
1359
|
} catch {
|
|
@@ -1325,10 +1473,10 @@ function isIgnoredTool(toolName) {
|
|
|
1325
1473
|
var DAEMON_PORT = 7391;
|
|
1326
1474
|
var DAEMON_HOST = "127.0.0.1";
|
|
1327
1475
|
function isDaemonRunning() {
|
|
1328
|
-
const pidFile =
|
|
1329
|
-
if (
|
|
1476
|
+
const pidFile = import_path5.default.join(import_os2.default.homedir(), ".node9", "daemon.pid");
|
|
1477
|
+
if (import_fs3.default.existsSync(pidFile)) {
|
|
1330
1478
|
try {
|
|
1331
|
-
const { pid, port } = JSON.parse(
|
|
1479
|
+
const { pid, port } = JSON.parse(import_fs3.default.readFileSync(pidFile, "utf-8"));
|
|
1332
1480
|
if (port !== DAEMON_PORT) return false;
|
|
1333
1481
|
process.kill(pid, 0);
|
|
1334
1482
|
return true;
|
|
@@ -1348,9 +1496,9 @@ function isDaemonRunning() {
|
|
|
1348
1496
|
}
|
|
1349
1497
|
function getPersistentDecision(toolName) {
|
|
1350
1498
|
try {
|
|
1351
|
-
const file =
|
|
1352
|
-
if (!
|
|
1353
|
-
const decisions = JSON.parse(
|
|
1499
|
+
const file = import_path5.default.join(import_os2.default.homedir(), ".node9", "decisions.json");
|
|
1500
|
+
if (!import_fs3.default.existsSync(file)) return null;
|
|
1501
|
+
const decisions = JSON.parse(import_fs3.default.readFileSync(file, "utf-8"));
|
|
1354
1502
|
const d = decisions[toolName];
|
|
1355
1503
|
if (d === "allow" || d === "deny") return d;
|
|
1356
1504
|
} catch {
|
|
@@ -1432,7 +1580,7 @@ async function resolveViaDaemon(id, decision, internalToken) {
|
|
|
1432
1580
|
signal: AbortSignal.timeout(3e3)
|
|
1433
1581
|
});
|
|
1434
1582
|
}
|
|
1435
|
-
var ACTIVITY_SOCKET_PATH = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" :
|
|
1583
|
+
var ACTIVITY_SOCKET_PATH = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path5.default.join(import_os2.default.tmpdir(), "node9-activity.sock");
|
|
1436
1584
|
function notifyActivity(data) {
|
|
1437
1585
|
return new Promise((resolve) => {
|
|
1438
1586
|
try {
|
|
@@ -1494,7 +1642,9 @@ async function _authorizeHeadlessCore(toolName, args, allowTerminalFallback = fa
|
|
|
1494
1642
|
let policyMatchedWord;
|
|
1495
1643
|
let riskMetadata;
|
|
1496
1644
|
if (config.policy.dlp.enabled && (!isIgnoredTool(toolName) || config.policy.dlp.scanIgnoredTools)) {
|
|
1497
|
-
const
|
|
1645
|
+
const argsObj = args && typeof args === "object" && !Array.isArray(args) ? args : {};
|
|
1646
|
+
const filePath = String(argsObj.file_path ?? argsObj.path ?? argsObj.filename ?? "");
|
|
1647
|
+
const dlpMatch = (filePath ? scanFilePath(filePath) : null) ?? scanArgs(args);
|
|
1498
1648
|
if (dlpMatch) {
|
|
1499
1649
|
const dlpReason = `\u{1F6A8} DATA LOSS PREVENTION: ${dlpMatch.patternName} detected in field "${dlpMatch.fieldPath}" (${dlpMatch.redactedSample})`;
|
|
1500
1650
|
if (dlpMatch.severity === "block") {
|
|
@@ -1868,8 +2018,8 @@ REASON: Action blocked because no approval channels are available. (Native/Brows
|
|
|
1868
2018
|
}
|
|
1869
2019
|
function getConfig() {
|
|
1870
2020
|
if (cachedConfig) return cachedConfig;
|
|
1871
|
-
const globalPath =
|
|
1872
|
-
const projectPath =
|
|
2021
|
+
const globalPath = import_path5.default.join(import_os2.default.homedir(), ".node9", "config.json");
|
|
2022
|
+
const projectPath = import_path5.default.join(process.cwd(), "node9.config.json");
|
|
1873
2023
|
const globalConfig = tryLoadConfig(globalPath);
|
|
1874
2024
|
const projectConfig = tryLoadConfig(projectPath);
|
|
1875
2025
|
const mergedSettings = {
|
|
@@ -1964,10 +2114,10 @@ function getConfig() {
|
|
|
1964
2114
|
return cachedConfig;
|
|
1965
2115
|
}
|
|
1966
2116
|
function tryLoadConfig(filePath) {
|
|
1967
|
-
if (!
|
|
2117
|
+
if (!import_fs3.default.existsSync(filePath)) return null;
|
|
1968
2118
|
let raw;
|
|
1969
2119
|
try {
|
|
1970
|
-
raw = JSON.parse(
|
|
2120
|
+
raw = JSON.parse(import_fs3.default.readFileSync(filePath, "utf-8"));
|
|
1971
2121
|
} catch (err) {
|
|
1972
2122
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1973
2123
|
process.stderr.write(
|
|
@@ -2029,9 +2179,9 @@ function getCredentials() {
|
|
|
2029
2179
|
};
|
|
2030
2180
|
}
|
|
2031
2181
|
try {
|
|
2032
|
-
const credPath =
|
|
2033
|
-
if (
|
|
2034
|
-
const creds = JSON.parse(
|
|
2182
|
+
const credPath = import_path5.default.join(import_os2.default.homedir(), ".node9", "credentials.json");
|
|
2183
|
+
if (import_fs3.default.existsSync(credPath)) {
|
|
2184
|
+
const creds = JSON.parse(import_fs3.default.readFileSync(credPath, "utf-8"));
|
|
2035
2185
|
const profileName = process.env.NODE9_PROFILE || "default";
|
|
2036
2186
|
const profile = creds[profileName];
|
|
2037
2187
|
if (profile?.apiKey) {
|