@node9/proxy 2.14.1 → 2.14.3
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 +1548 -1386
- package/dist/cli.mjs +1479 -1319
- package/dist/dashboard.mjs +91 -11
- package/dist/index.js +230 -92
- package/dist/index.mjs +230 -92
- package/package.json +1 -1
package/dist/dashboard.mjs
CHANGED
|
@@ -1775,6 +1775,9 @@ function classifySsrf(host) {
|
|
|
1775
1775
|
return null;
|
|
1776
1776
|
}
|
|
1777
1777
|
}
|
|
1778
|
+
function stripTerminalEscapes(s) {
|
|
1779
|
+
return s.replace(TERMINAL_ESCAPE_RE, "");
|
|
1780
|
+
}
|
|
1778
1781
|
function isShieldVerdict(v) {
|
|
1779
1782
|
return v === "allow" || v === "review" || v === "block";
|
|
1780
1783
|
}
|
|
@@ -1936,7 +1939,7 @@ function matchCanaryArgs(args, values) {
|
|
|
1936
1939
|
return null;
|
|
1937
1940
|
}
|
|
1938
1941
|
}
|
|
1939
|
-
var B58, B58_INDEX, XPRV_VERSIONS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, GREP_SHAPE, PATTERN_VERBS, PATTERN_VERB_NAMES, GREP_FILE_OPERANDS, READER_VALUE_LETTERS, FILE_OPERAND_FLAGS, SCP_VALUE_FLAGS, TAR_VALUE_LETTERS, ZIP_VALUE_LETTERS, RSYNC_VALUE_LETTERS, RSYNC_SKIP, CP_VALUE_LETTERS, INSTALL_VALUE_LETTERS, COPY_VERBS, TAR_MODE_WORD, COPY_VERB_HEADS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, FS_OP_CACHE_MAX, fsOpCache, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, SOURCE_COMMANDS, SSRF_MAX_HOST, METADATA_ADDRESSES, METADATA_HOSTNAMES, v4Octets, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH2, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES;
|
|
1942
|
+
var B58, B58_INDEX, XPRV_VERSIONS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, GREP_SHAPE, PATTERN_VERBS, PATTERN_VERB_NAMES, GREP_FILE_OPERANDS, READER_VALUE_LETTERS, FILE_OPERAND_FLAGS, SCP_VALUE_FLAGS, TAR_VALUE_LETTERS, ZIP_VALUE_LETTERS, RSYNC_VALUE_LETTERS, RSYNC_SKIP, CP_VALUE_LETTERS, INSTALL_VALUE_LETTERS, COPY_VERBS, TAR_MODE_WORD, COPY_VERB_HEADS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, FS_OP_CACHE_MAX, fsOpCache, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, SOURCE_COMMANDS, SSRF_MAX_HOST, METADATA_ADDRESSES, METADATA_HOSTNAMES, v4Octets, TERMINAL_ESCAPE_RE, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH2, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES;
|
|
1940
1943
|
var init_dist = __esm({
|
|
1941
1944
|
"packages/policy-engine/dist/index.mjs"() {
|
|
1942
1945
|
"use strict";
|
|
@@ -3150,6 +3153,7 @@ var init_dist = __esm({
|
|
|
3150
3153
|
const p = a.split(".");
|
|
3151
3154
|
return p.length === 4 ? p.map(Number) : null;
|
|
3152
3155
|
};
|
|
3156
|
+
TERMINAL_ESCAPE_RE = /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-_]|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
|
|
3153
3157
|
aws_default = {
|
|
3154
3158
|
name: "aws",
|
|
3155
3159
|
description: "Protects AWS infrastructure from destructive AI operations",
|
|
@@ -4656,6 +4660,51 @@ var init_trusted_hosts = __esm({
|
|
|
4656
4660
|
}
|
|
4657
4661
|
});
|
|
4658
4662
|
|
|
4663
|
+
// src/auth/api-url.ts
|
|
4664
|
+
function defaultHostSuffixes() {
|
|
4665
|
+
const host = new URL(DEFAULT_API_URL).hostname.toLowerCase();
|
|
4666
|
+
const parent = host.split(".").slice(1).join(".");
|
|
4667
|
+
return parent.includes(".") ? [host, parent] : [host];
|
|
4668
|
+
}
|
|
4669
|
+
function allowedSuffixes() {
|
|
4670
|
+
const extra = (process.env[HOST_ALLOW_ENV] ?? "").split(",").map(
|
|
4671
|
+
(s) => s.trim().toLowerCase().replace(/^\*?\./, "")
|
|
4672
|
+
).filter(Boolean);
|
|
4673
|
+
return [...defaultHostSuffixes(), ...extra];
|
|
4674
|
+
}
|
|
4675
|
+
function validateApiUrl(raw) {
|
|
4676
|
+
if (typeof raw !== "string" || raw.length === 0 || raw.length > 2048) return null;
|
|
4677
|
+
let u;
|
|
4678
|
+
try {
|
|
4679
|
+
u = new URL(raw);
|
|
4680
|
+
} catch {
|
|
4681
|
+
return null;
|
|
4682
|
+
}
|
|
4683
|
+
if (u.username || u.password) return null;
|
|
4684
|
+
if (u.protocol !== "https:" && u.protocol !== "http:") return null;
|
|
4685
|
+
const host = u.hostname.toLowerCase();
|
|
4686
|
+
if (LOOPBACK.has(host)) return u;
|
|
4687
|
+
if (u.protocol !== "https:") return null;
|
|
4688
|
+
const suffixes = allowedSuffixes();
|
|
4689
|
+
const ok = suffixes.some((s) => host === s || host.endsWith("." + s));
|
|
4690
|
+
return ok ? u : null;
|
|
4691
|
+
}
|
|
4692
|
+
function safeApiUrl(raw, onReject) {
|
|
4693
|
+
const ok = validateApiUrl(raw);
|
|
4694
|
+
if (ok) return typeof raw === "string" ? raw : ok.toString();
|
|
4695
|
+
onReject?.(raw);
|
|
4696
|
+
return DEFAULT_API_URL;
|
|
4697
|
+
}
|
|
4698
|
+
var DEFAULT_API_URL, LOOPBACK, HOST_ALLOW_ENV;
|
|
4699
|
+
var init_api_url = __esm({
|
|
4700
|
+
"src/auth/api-url.ts"() {
|
|
4701
|
+
"use strict";
|
|
4702
|
+
DEFAULT_API_URL = "https://api.node9.ai/api/v1/intercept";
|
|
4703
|
+
LOOPBACK = /* @__PURE__ */ new Set(["127.0.0.1", "localhost", "::1", "[::1]", "0.0.0.0"]);
|
|
4704
|
+
HOST_ALLOW_ENV = "NODE9_API_HOST_ALLOW";
|
|
4705
|
+
}
|
|
4706
|
+
});
|
|
4707
|
+
|
|
4659
4708
|
// src/config/index.ts
|
|
4660
4709
|
import fs6 from "fs";
|
|
4661
4710
|
import path7 from "path";
|
|
@@ -4674,12 +4723,26 @@ function sanitizeSsrfAllow(entries, source) {
|
|
|
4674
4723
|
}
|
|
4675
4724
|
return kept;
|
|
4676
4725
|
}
|
|
4726
|
+
function noteRejectedApiUrl(raw) {
|
|
4727
|
+
const key = String(raw).slice(0, 200);
|
|
4728
|
+
if (rejectedApiUrlsSeen.has(key)) return;
|
|
4729
|
+
rejectedApiUrlsSeen.add(key);
|
|
4730
|
+
try {
|
|
4731
|
+
fs6.appendFileSync(
|
|
4732
|
+
path7.join(os7.homedir(), ".node9", "hook-debug.log"),
|
|
4733
|
+
`[${(/* @__PURE__ */ new Date()).toISOString()}] REFUSED apiUrl, using the default instead: ${String(raw).slice(0, 200).replace(/[\r\n]+/g, " ")}
|
|
4734
|
+
`
|
|
4735
|
+
);
|
|
4736
|
+
} catch {
|
|
4737
|
+
}
|
|
4738
|
+
}
|
|
4677
4739
|
function getCredentials() {
|
|
4678
|
-
const DEFAULT_API_URL = "https://api.node9.ai/api/v1/intercept";
|
|
4679
4740
|
if (process.env.NODE9_API_KEY) {
|
|
4680
4741
|
return {
|
|
4681
4742
|
apiKey: process.env.NODE9_API_KEY,
|
|
4682
|
-
|
|
4743
|
+
// NODE9_API_URL is env, and a hook inherits the agent's env, so it is
|
|
4744
|
+
// no more trusted than the file below.
|
|
4745
|
+
apiUrl: safeApiUrl(process.env.NODE9_API_URL || DEFAULT_API_URL, noteRejectedApiUrl)
|
|
4683
4746
|
};
|
|
4684
4747
|
}
|
|
4685
4748
|
try {
|
|
@@ -4691,14 +4754,14 @@ function getCredentials() {
|
|
|
4691
4754
|
if (profile?.apiKey) {
|
|
4692
4755
|
return {
|
|
4693
4756
|
apiKey: profile.apiKey,
|
|
4694
|
-
apiUrl: profile.apiUrl || DEFAULT_API_URL,
|
|
4757
|
+
apiUrl: safeApiUrl(profile.apiUrl || DEFAULT_API_URL, noteRejectedApiUrl),
|
|
4695
4758
|
localOnly: profile.localOnly === true || profileName !== "default"
|
|
4696
4759
|
};
|
|
4697
4760
|
}
|
|
4698
4761
|
if (creds.apiKey) {
|
|
4699
4762
|
return {
|
|
4700
4763
|
apiKey: creds.apiKey,
|
|
4701
|
-
apiUrl: creds.apiUrl || DEFAULT_API_URL,
|
|
4764
|
+
apiUrl: safeApiUrl(creds.apiUrl || DEFAULT_API_URL, noteRejectedApiUrl),
|
|
4702
4765
|
localOnly: creds.localOnly === true
|
|
4703
4766
|
};
|
|
4704
4767
|
}
|
|
@@ -5328,7 +5391,7 @@ ${error.replace("Invalid config:\n", "")}
|
|
|
5328
5391
|
}
|
|
5329
5392
|
return sanitized;
|
|
5330
5393
|
}
|
|
5331
|
-
var DANGEROUS_WORDS, DEFAULT_CONFIG, RM_SAFE_PATH_PATTERN, VERDICT_ORDER, ADVISORY_SMART_RULES, cachedConfig, lastParsedRulesCache, CACHE_LOG_REARM_MS, cacheReadLastLoggedAt;
|
|
5394
|
+
var DANGEROUS_WORDS, DEFAULT_CONFIG, RM_SAFE_PATH_PATTERN, VERDICT_ORDER, ADVISORY_SMART_RULES, cachedConfig, rejectedApiUrlsSeen, lastParsedRulesCache, CACHE_LOG_REARM_MS, cacheReadLastLoggedAt;
|
|
5332
5395
|
var init_config = __esm({
|
|
5333
5396
|
"src/config/index.ts"() {
|
|
5334
5397
|
"use strict";
|
|
@@ -5339,6 +5402,7 @@ var init_config = __esm({
|
|
|
5339
5402
|
init_trusted_hosts();
|
|
5340
5403
|
init_dist();
|
|
5341
5404
|
init_dist();
|
|
5405
|
+
init_api_url();
|
|
5342
5406
|
DANGEROUS_WORDS = [
|
|
5343
5407
|
"mkfs",
|
|
5344
5408
|
// formats/wipes a filesystem partition
|
|
@@ -5612,6 +5676,7 @@ var init_config = __esm({
|
|
|
5612
5676
|
}
|
|
5613
5677
|
];
|
|
5614
5678
|
cachedConfig = null;
|
|
5679
|
+
rejectedApiUrlsSeen = /* @__PURE__ */ new Set();
|
|
5615
5680
|
lastParsedRulesCache = null;
|
|
5616
5681
|
CACHE_LOG_REARM_MS = 5 * 60 * 1e3;
|
|
5617
5682
|
cacheReadLastLoggedAt = 0;
|
|
@@ -5678,6 +5743,14 @@ var init_session_files = __esm({
|
|
|
5678
5743
|
}
|
|
5679
5744
|
});
|
|
5680
5745
|
|
|
5746
|
+
// src/utils/safe-text.ts
|
|
5747
|
+
var init_safe_text = __esm({
|
|
5748
|
+
"src/utils/safe-text.ts"() {
|
|
5749
|
+
"use strict";
|
|
5750
|
+
init_dist();
|
|
5751
|
+
}
|
|
5752
|
+
});
|
|
5753
|
+
|
|
5681
5754
|
// src/costSync.ts
|
|
5682
5755
|
function decodeProjectDirName(dirName) {
|
|
5683
5756
|
return dirName.replace(/-/g, "/");
|
|
@@ -5693,6 +5766,7 @@ var init_costSync = __esm({
|
|
|
5693
5766
|
init_cost_gemini();
|
|
5694
5767
|
init_cost_copilot();
|
|
5695
5768
|
init_session_files();
|
|
5769
|
+
init_safe_text();
|
|
5696
5770
|
SYNC_INTERVAL_MS = 10 * 60 * 1e3;
|
|
5697
5771
|
}
|
|
5698
5772
|
});
|
|
@@ -6602,6 +6676,13 @@ var init_setup_pi_shim = __esm({
|
|
|
6602
6676
|
}
|
|
6603
6677
|
});
|
|
6604
6678
|
|
|
6679
|
+
// src/utils/atomic-write.ts
|
|
6680
|
+
var init_atomic_write = __esm({
|
|
6681
|
+
"src/utils/atomic-write.ts"() {
|
|
6682
|
+
"use strict";
|
|
6683
|
+
}
|
|
6684
|
+
});
|
|
6685
|
+
|
|
6605
6686
|
// src/setup.ts
|
|
6606
6687
|
import chalk3 from "chalk";
|
|
6607
6688
|
import { confirm as rawConfirm } from "@inquirer/prompts";
|
|
@@ -6614,6 +6695,7 @@ var init_setup = __esm({
|
|
|
6614
6695
|
init_hook_baseline();
|
|
6615
6696
|
init_setup_opencode_shim();
|
|
6616
6697
|
init_setup_pi_shim();
|
|
6698
|
+
init_atomic_write();
|
|
6617
6699
|
}
|
|
6618
6700
|
});
|
|
6619
6701
|
|
|
@@ -6638,6 +6720,7 @@ var init_scan_json = __esm({
|
|
|
6638
6720
|
var init_scan_history = __esm({
|
|
6639
6721
|
"src/cli/render/scan-history.ts"() {
|
|
6640
6722
|
"use strict";
|
|
6723
|
+
init_atomic_write();
|
|
6641
6724
|
}
|
|
6642
6725
|
});
|
|
6643
6726
|
|
|
@@ -6666,9 +6749,6 @@ function isNode9SelfOutput(text) {
|
|
|
6666
6749
|
}
|
|
6667
6750
|
return false;
|
|
6668
6751
|
}
|
|
6669
|
-
function stripTerminalEscapes(s) {
|
|
6670
|
-
return s.replace(TERMINAL_ESCAPE_RE, "");
|
|
6671
|
-
}
|
|
6672
6752
|
function preview(input, max) {
|
|
6673
6753
|
const cmd = input.command ?? input.query ?? input.file_path ?? JSON.stringify(input);
|
|
6674
6754
|
const s = stripTerminalEscapes(String(cmd)).replace(/\s+/g, " ").trim();
|
|
@@ -7644,7 +7724,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
7644
7724
|
}
|
|
7645
7725
|
return result;
|
|
7646
7726
|
}
|
|
7647
|
-
var toolInspectionMap, CODE_EXTENSIONS, SELF_OUTPUT_MARKERS,
|
|
7727
|
+
var toolInspectionMap, CODE_EXTENSIONS, SELF_OUTPUT_MARKERS, LOOP_TOOLS, LOOP_THRESHOLD, LOOP_TIMESPAN_THRESHOLD_MS;
|
|
7648
7728
|
var init_scan = __esm({
|
|
7649
7729
|
"src/cli/commands/scan.ts"() {
|
|
7650
7730
|
"use strict";
|
|
@@ -7670,6 +7750,7 @@ var init_scan = __esm({
|
|
|
7670
7750
|
init_scan_json();
|
|
7671
7751
|
init_session_files();
|
|
7672
7752
|
init_scan_history();
|
|
7753
|
+
init_safe_text();
|
|
7673
7754
|
toolInspectionMap = DEFAULT_CONFIG.policy.toolInspection;
|
|
7674
7755
|
CODE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
7675
7756
|
".ts",
|
|
@@ -7704,7 +7785,6 @@ var init_scan = __esm({
|
|
|
7704
7785
|
/\bseverity:\s*['"](?:block|review|allow)['"]/,
|
|
7705
7786
|
/NODE9 SECURITY ALERT/
|
|
7706
7787
|
];
|
|
7707
|
-
TERMINAL_ESCAPE_RE = /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-_]|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
|
|
7708
7788
|
LOOP_TOOLS = /* @__PURE__ */ new Set([
|
|
7709
7789
|
"bash",
|
|
7710
7790
|
"execute_bash",
|