@node9/proxy 1.1.0 → 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/dist/cli.js +35 -46
- package/dist/cli.mjs +35 -46
- package/dist/index.js +11 -35
- package/dist/index.mjs +11 -35
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -790,7 +790,9 @@ var init_dlp = __esm({
|
|
|
790
790
|
DLP_PATTERNS = [
|
|
791
791
|
{ name: "AWS Access Key ID", regex: /\bAKIA[0-9A-Z]{16}\b/, severity: "block" },
|
|
792
792
|
{ name: "GitHub Token", regex: /\bgh[pous]_[A-Za-z0-9]{36}\b/, severity: "block" },
|
|
793
|
-
|
|
793
|
+
// Slack bot tokens: xoxb- + variable segment. Real tokens are ~50–80 chars;
|
|
794
|
+
// lower bound 20 avoids false negatives on partial tokens, upper 100 caps scan cost.
|
|
795
|
+
{ name: "Slack Bot Token", regex: /\bxoxb-[0-9A-Za-z-]{20,100}\b/, severity: "block" },
|
|
794
796
|
{ name: "OpenAI API Key", regex: /\bsk-[a-zA-Z0-9_-]{20,}\b/, severity: "block" },
|
|
795
797
|
{ name: "Stripe Secret Key", regex: /\bsk_(?:live|test)_[0-9a-zA-Z]{24}\b/, severity: "block" },
|
|
796
798
|
{
|
|
@@ -878,40 +880,13 @@ function resumeNode9() {
|
|
|
878
880
|
function validateRegex(pattern) {
|
|
879
881
|
if (!pattern) return "Pattern is required";
|
|
880
882
|
if (pattern.length > MAX_REGEX_LENGTH) return `Pattern exceeds max length of ${MAX_REGEX_LENGTH}`;
|
|
881
|
-
let parens = 0, brackets = 0, isEscaped = false, inCharClass = false;
|
|
882
|
-
for (let i = 0; i < pattern.length; i++) {
|
|
883
|
-
const char = pattern[i];
|
|
884
|
-
if (isEscaped) {
|
|
885
|
-
isEscaped = false;
|
|
886
|
-
continue;
|
|
887
|
-
}
|
|
888
|
-
if (char === "\\") {
|
|
889
|
-
isEscaped = true;
|
|
890
|
-
continue;
|
|
891
|
-
}
|
|
892
|
-
if (char === "[" && !inCharClass) {
|
|
893
|
-
inCharClass = true;
|
|
894
|
-
brackets++;
|
|
895
|
-
continue;
|
|
896
|
-
}
|
|
897
|
-
if (char === "]" && inCharClass) {
|
|
898
|
-
inCharClass = false;
|
|
899
|
-
brackets--;
|
|
900
|
-
continue;
|
|
901
|
-
}
|
|
902
|
-
if (inCharClass) continue;
|
|
903
|
-
if (char === "(") parens++;
|
|
904
|
-
else if (char === ")") parens--;
|
|
905
|
-
}
|
|
906
|
-
if (parens !== 0) return "Unbalanced parentheses";
|
|
907
|
-
if (brackets !== 0) return "Unbalanced brackets";
|
|
908
|
-
if (/\\\d+[*+{]/.test(pattern)) return "Quantified backreferences are forbidden (ReDoS risk)";
|
|
909
|
-
if (!(0, import_safe_regex2.default)(pattern)) return "Pattern rejected: potential ReDoS vulnerability detected";
|
|
910
883
|
try {
|
|
911
884
|
new RegExp(pattern);
|
|
912
885
|
} catch (e) {
|
|
913
886
|
return `Invalid regex syntax: ${e.message}`;
|
|
914
887
|
}
|
|
888
|
+
if (/\\\d+[*+{]/.test(pattern)) return "Quantified backreferences are forbidden (ReDoS risk)";
|
|
889
|
+
if (!(0, import_safe_regex2.default)(pattern)) return "Pattern rejected: potential ReDoS vulnerability detected";
|
|
915
890
|
return null;
|
|
916
891
|
}
|
|
917
892
|
function getCompiledRegex(pattern, flags = "") {
|
|
@@ -2125,10 +2100,10 @@ REASON: Action blocked because no approval channels are available. (Native/Brows
|
|
|
2125
2100
|
}
|
|
2126
2101
|
return finalResult;
|
|
2127
2102
|
}
|
|
2128
|
-
function getConfig() {
|
|
2129
|
-
if (cachedConfig) return cachedConfig;
|
|
2103
|
+
function getConfig(cwd) {
|
|
2104
|
+
if (!cwd && cachedConfig) return cachedConfig;
|
|
2130
2105
|
const globalPath = import_path5.default.join(import_os2.default.homedir(), ".node9", "config.json");
|
|
2131
|
-
const projectPath = import_path5.default.join(process.cwd(), "node9.config.json");
|
|
2106
|
+
const projectPath = import_path5.default.join(cwd ?? process.cwd(), "node9.config.json");
|
|
2132
2107
|
const globalConfig = tryLoadConfig(globalPath);
|
|
2133
2108
|
const projectConfig = tryLoadConfig(projectPath);
|
|
2134
2109
|
const mergedSettings = {
|
|
@@ -2215,12 +2190,13 @@ function getConfig() {
|
|
|
2215
2190
|
mergedPolicy.snapshot.tools = [...new Set(mergedPolicy.snapshot.tools)];
|
|
2216
2191
|
mergedPolicy.snapshot.onlyPaths = [...new Set(mergedPolicy.snapshot.onlyPaths)];
|
|
2217
2192
|
mergedPolicy.snapshot.ignorePaths = [...new Set(mergedPolicy.snapshot.ignorePaths)];
|
|
2218
|
-
|
|
2193
|
+
const result = {
|
|
2219
2194
|
settings: mergedSettings,
|
|
2220
2195
|
policy: mergedPolicy,
|
|
2221
2196
|
environments: mergedEnvironments
|
|
2222
2197
|
};
|
|
2223
|
-
|
|
2198
|
+
if (!cwd) cachedConfig = result;
|
|
2199
|
+
return result;
|
|
2224
2200
|
}
|
|
2225
2201
|
function tryLoadConfig(filePath) {
|
|
2226
2202
|
if (!import_fs3.default.existsSync(filePath)) return null;
|
|
@@ -5683,9 +5659,19 @@ function applyUndo(hash, cwd) {
|
|
|
5683
5659
|
env,
|
|
5684
5660
|
timeout: GIT_TIMEOUT
|
|
5685
5661
|
});
|
|
5662
|
+
if (lsTree.status !== 0) {
|
|
5663
|
+
process.stderr.write(`[Node9] applyUndo: git ls-tree failed for hash ${hash}
|
|
5664
|
+
`);
|
|
5665
|
+
return false;
|
|
5666
|
+
}
|
|
5686
5667
|
const snapshotFiles = new Set(
|
|
5687
5668
|
lsTree.stdout?.toString().trim().split("\n").filter(Boolean) ?? []
|
|
5688
5669
|
);
|
|
5670
|
+
if (snapshotFiles.size === 0) {
|
|
5671
|
+
process.stderr.write(`[Node9] applyUndo: ls-tree returned no files for hash ${hash}
|
|
5672
|
+
`);
|
|
5673
|
+
return false;
|
|
5674
|
+
}
|
|
5689
5675
|
const tracked = (0, import_child_process4.spawnSync)("git", ["ls-files"], { cwd: dir, env, timeout: GIT_TIMEOUT }).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
|
|
5690
5676
|
const untracked = (0, import_child_process4.spawnSync)("git", ["ls-files", "--others", "--exclude-standard"], {
|
|
5691
5677
|
cwd: dir,
|
|
@@ -5840,7 +5826,7 @@ async function runProxy(targetCommand) {
|
|
|
5840
5826
|
if (stdout) executable = stdout.trim();
|
|
5841
5827
|
} catch {
|
|
5842
5828
|
}
|
|
5843
|
-
console.
|
|
5829
|
+
console.error(import_chalk6.default.green(`\u{1F680} Node9 Proxy Active: Monitoring [${targetCommand}]`));
|
|
5844
5830
|
const child = (0, import_child_process6.spawn)(executable, args, {
|
|
5845
5831
|
stdio: ["pipe", "pipe", "inherit"],
|
|
5846
5832
|
// We control STDIN and STDOUT
|
|
@@ -6511,14 +6497,7 @@ RAW: ${raw}
|
|
|
6511
6497
|
}
|
|
6512
6498
|
process.exit(0);
|
|
6513
6499
|
}
|
|
6514
|
-
|
|
6515
|
-
try {
|
|
6516
|
-
process.chdir(payload.cwd);
|
|
6517
|
-
_resetConfigCache();
|
|
6518
|
-
} catch {
|
|
6519
|
-
}
|
|
6520
|
-
}
|
|
6521
|
-
const config = getConfig();
|
|
6500
|
+
const config = getConfig(payload.cwd || void 0);
|
|
6522
6501
|
if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
|
|
6523
6502
|
const logPath = import_path10.default.join(import_os7.default.homedir(), ".node9", "hook-debug.log");
|
|
6524
6503
|
if (!import_fs8.default.existsSync(import_path10.default.dirname(logPath)))
|
|
@@ -6651,11 +6630,21 @@ program.command("log").description("PostToolUse hook \u2014 records executed too
|
|
|
6651
6630
|
if (!import_fs8.default.existsSync(import_path10.default.dirname(logPath)))
|
|
6652
6631
|
import_fs8.default.mkdirSync(import_path10.default.dirname(logPath), { recursive: true });
|
|
6653
6632
|
import_fs8.default.appendFileSync(logPath, JSON.stringify(entry) + "\n");
|
|
6654
|
-
const
|
|
6633
|
+
const safeCwd = typeof payload.cwd === "string" && import_path10.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
6634
|
+
const config = getConfig(safeCwd);
|
|
6655
6635
|
if (shouldSnapshot(tool, {}, config)) {
|
|
6656
6636
|
await createShadowSnapshot("unknown", {}, config.policy.snapshot.ignorePaths);
|
|
6657
6637
|
}
|
|
6658
|
-
} catch {
|
|
6638
|
+
} catch (err) {
|
|
6639
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
6640
|
+
process.stderr.write(`[Node9] audit log error: ${msg}
|
|
6641
|
+
`);
|
|
6642
|
+
const debugPath = import_path10.default.join(import_os7.default.homedir(), ".node9", "hook-debug.log");
|
|
6643
|
+
try {
|
|
6644
|
+
import_fs8.default.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
|
|
6645
|
+
`);
|
|
6646
|
+
} catch {
|
|
6647
|
+
}
|
|
6659
6648
|
}
|
|
6660
6649
|
process.exit(0);
|
|
6661
6650
|
};
|
package/dist/cli.mjs
CHANGED
|
@@ -769,7 +769,9 @@ var init_dlp = __esm({
|
|
|
769
769
|
DLP_PATTERNS = [
|
|
770
770
|
{ name: "AWS Access Key ID", regex: /\bAKIA[0-9A-Z]{16}\b/, severity: "block" },
|
|
771
771
|
{ name: "GitHub Token", regex: /\bgh[pous]_[A-Za-z0-9]{36}\b/, severity: "block" },
|
|
772
|
-
|
|
772
|
+
// Slack bot tokens: xoxb- + variable segment. Real tokens are ~50–80 chars;
|
|
773
|
+
// lower bound 20 avoids false negatives on partial tokens, upper 100 caps scan cost.
|
|
774
|
+
{ name: "Slack Bot Token", regex: /\bxoxb-[0-9A-Za-z-]{20,100}\b/, severity: "block" },
|
|
773
775
|
{ name: "OpenAI API Key", regex: /\bsk-[a-zA-Z0-9_-]{20,}\b/, severity: "block" },
|
|
774
776
|
{ name: "Stripe Secret Key", regex: /\bsk_(?:live|test)_[0-9a-zA-Z]{24}\b/, severity: "block" },
|
|
775
777
|
{
|
|
@@ -868,40 +870,13 @@ function resumeNode9() {
|
|
|
868
870
|
function validateRegex(pattern) {
|
|
869
871
|
if (!pattern) return "Pattern is required";
|
|
870
872
|
if (pattern.length > MAX_REGEX_LENGTH) return `Pattern exceeds max length of ${MAX_REGEX_LENGTH}`;
|
|
871
|
-
let parens = 0, brackets = 0, isEscaped = false, inCharClass = false;
|
|
872
|
-
for (let i = 0; i < pattern.length; i++) {
|
|
873
|
-
const char = pattern[i];
|
|
874
|
-
if (isEscaped) {
|
|
875
|
-
isEscaped = false;
|
|
876
|
-
continue;
|
|
877
|
-
}
|
|
878
|
-
if (char === "\\") {
|
|
879
|
-
isEscaped = true;
|
|
880
|
-
continue;
|
|
881
|
-
}
|
|
882
|
-
if (char === "[" && !inCharClass) {
|
|
883
|
-
inCharClass = true;
|
|
884
|
-
brackets++;
|
|
885
|
-
continue;
|
|
886
|
-
}
|
|
887
|
-
if (char === "]" && inCharClass) {
|
|
888
|
-
inCharClass = false;
|
|
889
|
-
brackets--;
|
|
890
|
-
continue;
|
|
891
|
-
}
|
|
892
|
-
if (inCharClass) continue;
|
|
893
|
-
if (char === "(") parens++;
|
|
894
|
-
else if (char === ")") parens--;
|
|
895
|
-
}
|
|
896
|
-
if (parens !== 0) return "Unbalanced parentheses";
|
|
897
|
-
if (brackets !== 0) return "Unbalanced brackets";
|
|
898
|
-
if (/\\\d+[*+{]/.test(pattern)) return "Quantified backreferences are forbidden (ReDoS risk)";
|
|
899
|
-
if (!safeRegex(pattern)) return "Pattern rejected: potential ReDoS vulnerability detected";
|
|
900
873
|
try {
|
|
901
874
|
new RegExp(pattern);
|
|
902
875
|
} catch (e) {
|
|
903
876
|
return `Invalid regex syntax: ${e.message}`;
|
|
904
877
|
}
|
|
878
|
+
if (/\\\d+[*+{]/.test(pattern)) return "Quantified backreferences are forbidden (ReDoS risk)";
|
|
879
|
+
if (!safeRegex(pattern)) return "Pattern rejected: potential ReDoS vulnerability detected";
|
|
905
880
|
return null;
|
|
906
881
|
}
|
|
907
882
|
function getCompiledRegex(pattern, flags = "") {
|
|
@@ -2115,10 +2090,10 @@ REASON: Action blocked because no approval channels are available. (Native/Brows
|
|
|
2115
2090
|
}
|
|
2116
2091
|
return finalResult;
|
|
2117
2092
|
}
|
|
2118
|
-
function getConfig() {
|
|
2119
|
-
if (cachedConfig) return cachedConfig;
|
|
2093
|
+
function getConfig(cwd) {
|
|
2094
|
+
if (!cwd && cachedConfig) return cachedConfig;
|
|
2120
2095
|
const globalPath = path5.join(os2.homedir(), ".node9", "config.json");
|
|
2121
|
-
const projectPath = path5.join(process.cwd(), "node9.config.json");
|
|
2096
|
+
const projectPath = path5.join(cwd ?? process.cwd(), "node9.config.json");
|
|
2122
2097
|
const globalConfig = tryLoadConfig(globalPath);
|
|
2123
2098
|
const projectConfig = tryLoadConfig(projectPath);
|
|
2124
2099
|
const mergedSettings = {
|
|
@@ -2205,12 +2180,13 @@ function getConfig() {
|
|
|
2205
2180
|
mergedPolicy.snapshot.tools = [...new Set(mergedPolicy.snapshot.tools)];
|
|
2206
2181
|
mergedPolicy.snapshot.onlyPaths = [...new Set(mergedPolicy.snapshot.onlyPaths)];
|
|
2207
2182
|
mergedPolicy.snapshot.ignorePaths = [...new Set(mergedPolicy.snapshot.ignorePaths)];
|
|
2208
|
-
|
|
2183
|
+
const result = {
|
|
2209
2184
|
settings: mergedSettings,
|
|
2210
2185
|
policy: mergedPolicy,
|
|
2211
2186
|
environments: mergedEnvironments
|
|
2212
2187
|
};
|
|
2213
|
-
|
|
2188
|
+
if (!cwd) cachedConfig = result;
|
|
2189
|
+
return result;
|
|
2214
2190
|
}
|
|
2215
2191
|
function tryLoadConfig(filePath) {
|
|
2216
2192
|
if (!fs3.existsSync(filePath)) return null;
|
|
@@ -5662,9 +5638,19 @@ function applyUndo(hash, cwd) {
|
|
|
5662
5638
|
env,
|
|
5663
5639
|
timeout: GIT_TIMEOUT
|
|
5664
5640
|
});
|
|
5641
|
+
if (lsTree.status !== 0) {
|
|
5642
|
+
process.stderr.write(`[Node9] applyUndo: git ls-tree failed for hash ${hash}
|
|
5643
|
+
`);
|
|
5644
|
+
return false;
|
|
5645
|
+
}
|
|
5665
5646
|
const snapshotFiles = new Set(
|
|
5666
5647
|
lsTree.stdout?.toString().trim().split("\n").filter(Boolean) ?? []
|
|
5667
5648
|
);
|
|
5649
|
+
if (snapshotFiles.size === 0) {
|
|
5650
|
+
process.stderr.write(`[Node9] applyUndo: ls-tree returned no files for hash ${hash}
|
|
5651
|
+
`);
|
|
5652
|
+
return false;
|
|
5653
|
+
}
|
|
5668
5654
|
const tracked = spawnSync3("git", ["ls-files"], { cwd: dir, env, timeout: GIT_TIMEOUT }).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
|
|
5669
5655
|
const untracked = spawnSync3("git", ["ls-files", "--others", "--exclude-standard"], {
|
|
5670
5656
|
cwd: dir,
|
|
@@ -5819,7 +5805,7 @@ async function runProxy(targetCommand) {
|
|
|
5819
5805
|
if (stdout) executable = stdout.trim();
|
|
5820
5806
|
} catch {
|
|
5821
5807
|
}
|
|
5822
|
-
console.
|
|
5808
|
+
console.error(chalk6.green(`\u{1F680} Node9 Proxy Active: Monitoring [${targetCommand}]`));
|
|
5823
5809
|
const child = spawn5(executable, args, {
|
|
5824
5810
|
stdio: ["pipe", "pipe", "inherit"],
|
|
5825
5811
|
// We control STDIN and STDOUT
|
|
@@ -6490,14 +6476,7 @@ RAW: ${raw}
|
|
|
6490
6476
|
}
|
|
6491
6477
|
process.exit(0);
|
|
6492
6478
|
}
|
|
6493
|
-
|
|
6494
|
-
try {
|
|
6495
|
-
process.chdir(payload.cwd);
|
|
6496
|
-
_resetConfigCache();
|
|
6497
|
-
} catch {
|
|
6498
|
-
}
|
|
6499
|
-
}
|
|
6500
|
-
const config = getConfig();
|
|
6479
|
+
const config = getConfig(payload.cwd || void 0);
|
|
6501
6480
|
if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
|
|
6502
6481
|
const logPath = path10.join(os7.homedir(), ".node9", "hook-debug.log");
|
|
6503
6482
|
if (!fs8.existsSync(path10.dirname(logPath)))
|
|
@@ -6630,11 +6609,21 @@ program.command("log").description("PostToolUse hook \u2014 records executed too
|
|
|
6630
6609
|
if (!fs8.existsSync(path10.dirname(logPath)))
|
|
6631
6610
|
fs8.mkdirSync(path10.dirname(logPath), { recursive: true });
|
|
6632
6611
|
fs8.appendFileSync(logPath, JSON.stringify(entry) + "\n");
|
|
6633
|
-
const
|
|
6612
|
+
const safeCwd = typeof payload.cwd === "string" && path10.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
6613
|
+
const config = getConfig(safeCwd);
|
|
6634
6614
|
if (shouldSnapshot(tool, {}, config)) {
|
|
6635
6615
|
await createShadowSnapshot("unknown", {}, config.policy.snapshot.ignorePaths);
|
|
6636
6616
|
}
|
|
6637
|
-
} catch {
|
|
6617
|
+
} catch (err) {
|
|
6618
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
6619
|
+
process.stderr.write(`[Node9] audit log error: ${msg}
|
|
6620
|
+
`);
|
|
6621
|
+
const debugPath = path10.join(os7.homedir(), ".node9", "hook-debug.log");
|
|
6622
|
+
try {
|
|
6623
|
+
fs8.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
|
|
6624
|
+
`);
|
|
6625
|
+
} catch {
|
|
6626
|
+
}
|
|
6638
6627
|
}
|
|
6639
6628
|
process.exit(0);
|
|
6640
6629
|
};
|
package/dist/index.js
CHANGED
|
@@ -685,7 +685,9 @@ var import_path4 = __toESM(require("path"));
|
|
|
685
685
|
var DLP_PATTERNS = [
|
|
686
686
|
{ name: "AWS Access Key ID", regex: /\bAKIA[0-9A-Z]{16}\b/, severity: "block" },
|
|
687
687
|
{ name: "GitHub Token", regex: /\bgh[pous]_[A-Za-z0-9]{36}\b/, severity: "block" },
|
|
688
|
-
|
|
688
|
+
// Slack bot tokens: xoxb- + variable segment. Real tokens are ~50–80 chars;
|
|
689
|
+
// lower bound 20 avoids false negatives on partial tokens, upper 100 caps scan cost.
|
|
690
|
+
{ name: "Slack Bot Token", regex: /\bxoxb-[0-9A-Za-z-]{20,100}\b/, severity: "block" },
|
|
689
691
|
{ name: "OpenAI API Key", regex: /\bsk-[a-zA-Z0-9_-]{20,}\b/, severity: "block" },
|
|
690
692
|
{ name: "Stripe Secret Key", regex: /\bsk_(?:live|test)_[0-9a-zA-Z]{24}\b/, severity: "block" },
|
|
691
693
|
{
|
|
@@ -853,40 +855,13 @@ var regexCache = /* @__PURE__ */ new Map();
|
|
|
853
855
|
function validateRegex(pattern) {
|
|
854
856
|
if (!pattern) return "Pattern is required";
|
|
855
857
|
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
858
|
try {
|
|
886
859
|
new RegExp(pattern);
|
|
887
860
|
} catch (e) {
|
|
888
861
|
return `Invalid regex syntax: ${e.message}`;
|
|
889
862
|
}
|
|
863
|
+
if (/\\\d+[*+{]/.test(pattern)) return "Quantified backreferences are forbidden (ReDoS risk)";
|
|
864
|
+
if (!(0, import_safe_regex2.default)(pattern)) return "Pattern rejected: potential ReDoS vulnerability detected";
|
|
890
865
|
return null;
|
|
891
866
|
}
|
|
892
867
|
function getCompiledRegex(pattern, flags = "") {
|
|
@@ -2016,10 +1991,10 @@ REASON: Action blocked because no approval channels are available. (Native/Brows
|
|
|
2016
1991
|
}
|
|
2017
1992
|
return finalResult;
|
|
2018
1993
|
}
|
|
2019
|
-
function getConfig() {
|
|
2020
|
-
if (cachedConfig) return cachedConfig;
|
|
1994
|
+
function getConfig(cwd) {
|
|
1995
|
+
if (!cwd && cachedConfig) return cachedConfig;
|
|
2021
1996
|
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");
|
|
1997
|
+
const projectPath = import_path5.default.join(cwd ?? process.cwd(), "node9.config.json");
|
|
2023
1998
|
const globalConfig = tryLoadConfig(globalPath);
|
|
2024
1999
|
const projectConfig = tryLoadConfig(projectPath);
|
|
2025
2000
|
const mergedSettings = {
|
|
@@ -2106,12 +2081,13 @@ function getConfig() {
|
|
|
2106
2081
|
mergedPolicy.snapshot.tools = [...new Set(mergedPolicy.snapshot.tools)];
|
|
2107
2082
|
mergedPolicy.snapshot.onlyPaths = [...new Set(mergedPolicy.snapshot.onlyPaths)];
|
|
2108
2083
|
mergedPolicy.snapshot.ignorePaths = [...new Set(mergedPolicy.snapshot.ignorePaths)];
|
|
2109
|
-
|
|
2084
|
+
const result = {
|
|
2110
2085
|
settings: mergedSettings,
|
|
2111
2086
|
policy: mergedPolicy,
|
|
2112
2087
|
environments: mergedEnvironments
|
|
2113
2088
|
};
|
|
2114
|
-
|
|
2089
|
+
if (!cwd) cachedConfig = result;
|
|
2090
|
+
return result;
|
|
2115
2091
|
}
|
|
2116
2092
|
function tryLoadConfig(filePath) {
|
|
2117
2093
|
if (!import_fs3.default.existsSync(filePath)) return null;
|
package/dist/index.mjs
CHANGED
|
@@ -649,7 +649,9 @@ import path4 from "path";
|
|
|
649
649
|
var DLP_PATTERNS = [
|
|
650
650
|
{ name: "AWS Access Key ID", regex: /\bAKIA[0-9A-Z]{16}\b/, severity: "block" },
|
|
651
651
|
{ name: "GitHub Token", regex: /\bgh[pous]_[A-Za-z0-9]{36}\b/, severity: "block" },
|
|
652
|
-
|
|
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" },
|
|
653
655
|
{ name: "OpenAI API Key", regex: /\bsk-[a-zA-Z0-9_-]{20,}\b/, severity: "block" },
|
|
654
656
|
{ name: "Stripe Secret Key", regex: /\bsk_(?:live|test)_[0-9a-zA-Z]{24}\b/, severity: "block" },
|
|
655
657
|
{
|
|
@@ -817,40 +819,13 @@ var regexCache = /* @__PURE__ */ new Map();
|
|
|
817
819
|
function validateRegex(pattern) {
|
|
818
820
|
if (!pattern) return "Pattern is required";
|
|
819
821
|
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
822
|
try {
|
|
850
823
|
new RegExp(pattern);
|
|
851
824
|
} catch (e) {
|
|
852
825
|
return `Invalid regex syntax: ${e.message}`;
|
|
853
826
|
}
|
|
827
|
+
if (/\\\d+[*+{]/.test(pattern)) return "Quantified backreferences are forbidden (ReDoS risk)";
|
|
828
|
+
if (!safeRegex(pattern)) return "Pattern rejected: potential ReDoS vulnerability detected";
|
|
854
829
|
return null;
|
|
855
830
|
}
|
|
856
831
|
function getCompiledRegex(pattern, flags = "") {
|
|
@@ -1980,10 +1955,10 @@ REASON: Action blocked because no approval channels are available. (Native/Brows
|
|
|
1980
1955
|
}
|
|
1981
1956
|
return finalResult;
|
|
1982
1957
|
}
|
|
1983
|
-
function getConfig() {
|
|
1984
|
-
if (cachedConfig) return cachedConfig;
|
|
1958
|
+
function getConfig(cwd) {
|
|
1959
|
+
if (!cwd && cachedConfig) return cachedConfig;
|
|
1985
1960
|
const globalPath = path5.join(os2.homedir(), ".node9", "config.json");
|
|
1986
|
-
const projectPath = path5.join(process.cwd(), "node9.config.json");
|
|
1961
|
+
const projectPath = path5.join(cwd ?? process.cwd(), "node9.config.json");
|
|
1987
1962
|
const globalConfig = tryLoadConfig(globalPath);
|
|
1988
1963
|
const projectConfig = tryLoadConfig(projectPath);
|
|
1989
1964
|
const mergedSettings = {
|
|
@@ -2070,12 +2045,13 @@ function getConfig() {
|
|
|
2070
2045
|
mergedPolicy.snapshot.tools = [...new Set(mergedPolicy.snapshot.tools)];
|
|
2071
2046
|
mergedPolicy.snapshot.onlyPaths = [...new Set(mergedPolicy.snapshot.onlyPaths)];
|
|
2072
2047
|
mergedPolicy.snapshot.ignorePaths = [...new Set(mergedPolicy.snapshot.ignorePaths)];
|
|
2073
|
-
|
|
2048
|
+
const result = {
|
|
2074
2049
|
settings: mergedSettings,
|
|
2075
2050
|
policy: mergedPolicy,
|
|
2076
2051
|
environments: mergedEnvironments
|
|
2077
2052
|
};
|
|
2078
|
-
|
|
2053
|
+
if (!cwd) cachedConfig = result;
|
|
2054
|
+
return result;
|
|
2079
2055
|
}
|
|
2080
2056
|
function tryLoadConfig(filePath) {
|
|
2081
2057
|
if (!fs3.existsSync(filePath)) return null;
|