@node9/proxy 2.9.2 → 2.9.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 CHANGED
@@ -2063,7 +2063,7 @@ function classifySsrf(host) {
2063
2063
  if (METADATA_ADDRESSES.has(ip)) return hit("metadata", false);
2064
2064
  const o = v4Octets(ip);
2065
2065
  if (o) {
2066
- if (o[0] === 0 && o[1] === 0 && o[2] === 0 && o[3] === 0) return hit("unspecified", false);
2066
+ if (o[0] === 0 && o[1] === 0 && o[2] === 0 && o[3] === 0) return hit("unspecified", true);
2067
2067
  if (o[0] === 169 && o[1] === 254) return hit("link-local", false);
2068
2068
  if (o[0] >= 224 && o[0] <= 239) return hit("multicast", false);
2069
2069
  if (o[0] === 100 && o[1] >= 64 && o[1] <= 127) return hit("cgnat", true);
@@ -2075,7 +2075,7 @@ function classifySsrf(host) {
2075
2075
  }
2076
2076
  const g = expandIpv6(ip);
2077
2077
  if (!g) return null;
2078
- if (g.every((x) => x === 0)) return hit("unspecified", false);
2078
+ if (g.every((x) => x === 0)) return hit("unspecified", true);
2079
2079
  if ((g[0] & 65472) === 65152) return hit("link-local", false);
2080
2080
  if ((g[0] & 65280) === 65280) return hit("multicast", false);
2081
2081
  if (g.slice(0, 7).every((x) => x === 0) && g[7] === 1) return hit("private", true);
@@ -2091,7 +2091,7 @@ function ssrfFloor(tokens, opts = {}) {
2091
2091
  for (const { token, binary } of tokens) {
2092
2092
  const m = classifySsrf(token);
2093
2093
  if (!m) continue;
2094
- if (m.tier === "private" && !opts.ssrfStrict) continue;
2094
+ if (STRICT_TIERS.has(m.tier) && !opts.ssrfStrict) continue;
2095
2095
  if (m.overridable && m.normalized && exempt2.has(m.normalized)) continue;
2096
2096
  return {
2097
2097
  ...m,
@@ -3205,7 +3205,7 @@ function* stringValues(obj, depth = 0) {
3205
3205
  }
3206
3206
  for (const v of Object.values(obj)) yield* stringValues(v, depth + 1);
3207
3207
  }
3208
- var import_safe_regex2, import_crypto3, import_mvdan_sh, import_picomatch, import_safe_regex22, import_safe_regex23, import_crypto4, IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, DLP_SCAN_LIMITS, 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, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_HEREDOC_OPS, DEFAULT_EGRESS_ALLOWLIST, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, SSRF_MAX_HOST, METADATA_ADDRESSES, METADATA_HOSTNAMES, v4Octets, TIER_REASON, VERDICT_RANK, SQL_DML_KEYWORDS, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, 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, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, TERMINAL_ESCAPE_RE, ENGINE_VERSION;
3208
+ var import_safe_regex2, import_crypto3, import_mvdan_sh, import_picomatch, import_safe_regex22, import_safe_regex23, import_crypto4, IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, DLP_SCAN_LIMITS, 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, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_HEREDOC_OPS, DEFAULT_EGRESS_ALLOWLIST, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, VERDICT_RANK, SQL_DML_KEYWORDS, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, 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, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, TERMINAL_ESCAPE_RE, ENGINE_VERSION;
3209
3209
  var init_dist = __esm({
3210
3210
  "packages/policy-engine/dist/index.mjs"() {
3211
3211
  "use strict";
@@ -4360,9 +4360,15 @@ var init_dist = __esm({
4360
4360
  // AWS ECS task role
4361
4361
  "168.63.129.16",
4362
4362
  // Azure WireServer
4363
- "fd00:ec2::254"
4363
+ "fd00:ec2::254",
4364
4364
  // AWS IMDS over IPv6 (inside fc00::/7, which is NOT a tier)
4365
+ // Alibaba Cloud IMDS. It sits INSIDE 100.64/10, so before this line it was
4366
+ // classified as cgnat and therefore exemptable: one ssrfAllow entry opened a
4367
+ // live credential endpoint. It has to be named here, above the range check,
4368
+ // and it is the reason relaxing cgnat is safe.
4369
+ "100.100.100.200"
4365
4370
  ]);
4371
+ STRICT_TIERS = /* @__PURE__ */ new Set(["private", "unspecified", "cgnat"]);
4366
4372
  METADATA_HOSTNAMES = /* @__PURE__ */ new Set(["metadata.google.internal", "metadata.goog", "metadata"]);
4367
4373
  v4Octets = (a) => {
4368
4374
  const p = a.split(".");
@@ -4372,7 +4378,7 @@ var init_dist = __esm({
4372
4378
  metadata: "a cloud instance-metadata endpoint, the classic credential-theft target",
4373
4379
  "link-local": "a link-local address",
4374
4380
  multicast: "a multicast address",
4375
- unspecified: "the unspecified address",
4381
+ unspecified: "the unspecified address, which reaches this host",
4376
4382
  cgnat: "a carrier-grade NAT address",
4377
4383
  private: "a loopback or private address"
4378
4384
  };
package/dist/cli.mjs CHANGED
@@ -2074,7 +2074,7 @@ function classifySsrf(host) {
2074
2074
  if (METADATA_ADDRESSES.has(ip)) return hit("metadata", false);
2075
2075
  const o = v4Octets(ip);
2076
2076
  if (o) {
2077
- if (o[0] === 0 && o[1] === 0 && o[2] === 0 && o[3] === 0) return hit("unspecified", false);
2077
+ if (o[0] === 0 && o[1] === 0 && o[2] === 0 && o[3] === 0) return hit("unspecified", true);
2078
2078
  if (o[0] === 169 && o[1] === 254) return hit("link-local", false);
2079
2079
  if (o[0] >= 224 && o[0] <= 239) return hit("multicast", false);
2080
2080
  if (o[0] === 100 && o[1] >= 64 && o[1] <= 127) return hit("cgnat", true);
@@ -2086,7 +2086,7 @@ function classifySsrf(host) {
2086
2086
  }
2087
2087
  const g = expandIpv6(ip);
2088
2088
  if (!g) return null;
2089
- if (g.every((x) => x === 0)) return hit("unspecified", false);
2089
+ if (g.every((x) => x === 0)) return hit("unspecified", true);
2090
2090
  if ((g[0] & 65472) === 65152) return hit("link-local", false);
2091
2091
  if ((g[0] & 65280) === 65280) return hit("multicast", false);
2092
2092
  if (g.slice(0, 7).every((x) => x === 0) && g[7] === 1) return hit("private", true);
@@ -2102,7 +2102,7 @@ function ssrfFloor(tokens, opts = {}) {
2102
2102
  for (const { token, binary } of tokens) {
2103
2103
  const m = classifySsrf(token);
2104
2104
  if (!m) continue;
2105
- if (m.tier === "private" && !opts.ssrfStrict) continue;
2105
+ if (STRICT_TIERS.has(m.tier) && !opts.ssrfStrict) continue;
2106
2106
  if (m.overridable && m.normalized && exempt2.has(m.normalized)) continue;
2107
2107
  return {
2108
2108
  ...m,
@@ -3216,7 +3216,7 @@ function* stringValues(obj, depth = 0) {
3216
3216
  }
3217
3217
  for (const v of Object.values(obj)) yield* stringValues(v, depth + 1);
3218
3218
  }
3219
- var IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, DLP_SCAN_LIMITS, 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, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_HEREDOC_OPS, DEFAULT_EGRESS_ALLOWLIST, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, SSRF_MAX_HOST, METADATA_ADDRESSES, METADATA_HOSTNAMES, v4Octets, TIER_REASON, VERDICT_RANK, SQL_DML_KEYWORDS, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, 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, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, TERMINAL_ESCAPE_RE, ENGINE_VERSION;
3219
+ var IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, DLP_SCAN_LIMITS, 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, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_HEREDOC_OPS, DEFAULT_EGRESS_ALLOWLIST, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, VERDICT_RANK, SQL_DML_KEYWORDS, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, 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, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, TERMINAL_ESCAPE_RE, ENGINE_VERSION;
3220
3220
  var init_dist = __esm({
3221
3221
  "packages/policy-engine/dist/index.mjs"() {
3222
3222
  "use strict";
@@ -4364,9 +4364,15 @@ var init_dist = __esm({
4364
4364
  // AWS ECS task role
4365
4365
  "168.63.129.16",
4366
4366
  // Azure WireServer
4367
- "fd00:ec2::254"
4367
+ "fd00:ec2::254",
4368
4368
  // AWS IMDS over IPv6 (inside fc00::/7, which is NOT a tier)
4369
+ // Alibaba Cloud IMDS. It sits INSIDE 100.64/10, so before this line it was
4370
+ // classified as cgnat and therefore exemptable: one ssrfAllow entry opened a
4371
+ // live credential endpoint. It has to be named here, above the range check,
4372
+ // and it is the reason relaxing cgnat is safe.
4373
+ "100.100.100.200"
4369
4374
  ]);
4375
+ STRICT_TIERS = /* @__PURE__ */ new Set(["private", "unspecified", "cgnat"]);
4370
4376
  METADATA_HOSTNAMES = /* @__PURE__ */ new Set(["metadata.google.internal", "metadata.goog", "metadata"]);
4371
4377
  v4Octets = (a) => {
4372
4378
  const p = a.split(".");
@@ -4376,7 +4382,7 @@ var init_dist = __esm({
4376
4382
  metadata: "a cloud instance-metadata endpoint, the classic credential-theft target",
4377
4383
  "link-local": "a link-local address",
4378
4384
  multicast: "a multicast address",
4379
- unspecified: "the unspecified address",
4385
+ unspecified: "the unspecified address, which reaches this host",
4380
4386
  cgnat: "a carrier-grade NAT address",
4381
4387
  private: "a loopback or private address"
4382
4388
  };
@@ -856,7 +856,7 @@ function classifySsrf(host) {
856
856
  if (METADATA_ADDRESSES.has(ip)) return hit("metadata", false);
857
857
  const o = v4Octets(ip);
858
858
  if (o) {
859
- if (o[0] === 0 && o[1] === 0 && o[2] === 0 && o[3] === 0) return hit("unspecified", false);
859
+ if (o[0] === 0 && o[1] === 0 && o[2] === 0 && o[3] === 0) return hit("unspecified", true);
860
860
  if (o[0] === 169 && o[1] === 254) return hit("link-local", false);
861
861
  if (o[0] >= 224 && o[0] <= 239) return hit("multicast", false);
862
862
  if (o[0] === 100 && o[1] >= 64 && o[1] <= 127) return hit("cgnat", true);
@@ -868,7 +868,7 @@ function classifySsrf(host) {
868
868
  }
869
869
  const g = expandIpv6(ip);
870
870
  if (!g) return null;
871
- if (g.every((x) => x === 0)) return hit("unspecified", false);
871
+ if (g.every((x) => x === 0)) return hit("unspecified", true);
872
872
  if ((g[0] & 65472) === 65152) return hit("link-local", false);
873
873
  if ((g[0] & 65280) === 65280) return hit("multicast", false);
874
874
  if (g.slice(0, 7).every((x) => x === 0) && g[7] === 1) return hit("private", true);
@@ -1759,8 +1759,13 @@ var init_dist = __esm({
1759
1759
  // AWS ECS task role
1760
1760
  "168.63.129.16",
1761
1761
  // Azure WireServer
1762
- "fd00:ec2::254"
1762
+ "fd00:ec2::254",
1763
1763
  // AWS IMDS over IPv6 (inside fc00::/7, which is NOT a tier)
1764
+ // Alibaba Cloud IMDS. It sits INSIDE 100.64/10, so before this line it was
1765
+ // classified as cgnat and therefore exemptable: one ssrfAllow entry opened a
1766
+ // live credential endpoint. It has to be named here, above the range check,
1767
+ // and it is the reason relaxing cgnat is safe.
1768
+ "100.100.100.200"
1764
1769
  ]);
1765
1770
  METADATA_HOSTNAMES = /* @__PURE__ */ new Set(["metadata.google.internal", "metadata.goog", "metadata"]);
1766
1771
  v4Octets = (a) => {
package/dist/index.js CHANGED
@@ -3117,9 +3117,15 @@ var METADATA_ADDRESSES = /* @__PURE__ */ new Set([
3117
3117
  // AWS ECS task role
3118
3118
  "168.63.129.16",
3119
3119
  // Azure WireServer
3120
- "fd00:ec2::254"
3120
+ "fd00:ec2::254",
3121
3121
  // AWS IMDS over IPv6 (inside fc00::/7, which is NOT a tier)
3122
+ // Alibaba Cloud IMDS. It sits INSIDE 100.64/10, so before this line it was
3123
+ // classified as cgnat and therefore exemptable: one ssrfAllow entry opened a
3124
+ // live credential endpoint. It has to be named here, above the range check,
3125
+ // and it is the reason relaxing cgnat is safe.
3126
+ "100.100.100.200"
3122
3127
  ]);
3128
+ var STRICT_TIERS = /* @__PURE__ */ new Set(["private", "unspecified", "cgnat"]);
3123
3129
  var METADATA_HOSTNAMES = /* @__PURE__ */ new Set(["metadata.google.internal", "metadata.goog", "metadata"]);
3124
3130
  var v4Octets = (a) => {
3125
3131
  const p = a.split(".");
@@ -3142,7 +3148,7 @@ function classifySsrf(host) {
3142
3148
  if (METADATA_ADDRESSES.has(ip)) return hit("metadata", false);
3143
3149
  const o = v4Octets(ip);
3144
3150
  if (o) {
3145
- if (o[0] === 0 && o[1] === 0 && o[2] === 0 && o[3] === 0) return hit("unspecified", false);
3151
+ if (o[0] === 0 && o[1] === 0 && o[2] === 0 && o[3] === 0) return hit("unspecified", true);
3146
3152
  if (o[0] === 169 && o[1] === 254) return hit("link-local", false);
3147
3153
  if (o[0] >= 224 && o[0] <= 239) return hit("multicast", false);
3148
3154
  if (o[0] === 100 && o[1] >= 64 && o[1] <= 127) return hit("cgnat", true);
@@ -3154,7 +3160,7 @@ function classifySsrf(host) {
3154
3160
  }
3155
3161
  const g = expandIpv6(ip);
3156
3162
  if (!g) return null;
3157
- if (g.every((x) => x === 0)) return hit("unspecified", false);
3163
+ if (g.every((x) => x === 0)) return hit("unspecified", true);
3158
3164
  if ((g[0] & 65472) === 65152) return hit("link-local", false);
3159
3165
  if ((g[0] & 65280) === 65280) return hit("multicast", false);
3160
3166
  if (g.slice(0, 7).every((x) => x === 0) && g[7] === 1) return hit("private", true);
@@ -3167,7 +3173,7 @@ var TIER_REASON = {
3167
3173
  metadata: "a cloud instance-metadata endpoint, the classic credential-theft target",
3168
3174
  "link-local": "a link-local address",
3169
3175
  multicast: "a multicast address",
3170
- unspecified: "the unspecified address",
3176
+ unspecified: "the unspecified address, which reaches this host",
3171
3177
  cgnat: "a carrier-grade NAT address",
3172
3178
  private: "a loopback or private address"
3173
3179
  };
@@ -3178,7 +3184,7 @@ function ssrfFloor(tokens, opts = {}) {
3178
3184
  for (const { token, binary } of tokens) {
3179
3185
  const m = classifySsrf(token);
3180
3186
  if (!m) continue;
3181
- if (m.tier === "private" && !opts.ssrfStrict) continue;
3187
+ if (STRICT_TIERS.has(m.tier) && !opts.ssrfStrict) continue;
3182
3188
  if (m.overridable && m.normalized && exempt.has(m.normalized)) continue;
3183
3189
  return {
3184
3190
  ...m,
package/dist/index.mjs CHANGED
@@ -3087,9 +3087,15 @@ var METADATA_ADDRESSES = /* @__PURE__ */ new Set([
3087
3087
  // AWS ECS task role
3088
3088
  "168.63.129.16",
3089
3089
  // Azure WireServer
3090
- "fd00:ec2::254"
3090
+ "fd00:ec2::254",
3091
3091
  // AWS IMDS over IPv6 (inside fc00::/7, which is NOT a tier)
3092
+ // Alibaba Cloud IMDS. It sits INSIDE 100.64/10, so before this line it was
3093
+ // classified as cgnat and therefore exemptable: one ssrfAllow entry opened a
3094
+ // live credential endpoint. It has to be named here, above the range check,
3095
+ // and it is the reason relaxing cgnat is safe.
3096
+ "100.100.100.200"
3092
3097
  ]);
3098
+ var STRICT_TIERS = /* @__PURE__ */ new Set(["private", "unspecified", "cgnat"]);
3093
3099
  var METADATA_HOSTNAMES = /* @__PURE__ */ new Set(["metadata.google.internal", "metadata.goog", "metadata"]);
3094
3100
  var v4Octets = (a) => {
3095
3101
  const p = a.split(".");
@@ -3112,7 +3118,7 @@ function classifySsrf(host) {
3112
3118
  if (METADATA_ADDRESSES.has(ip)) return hit("metadata", false);
3113
3119
  const o = v4Octets(ip);
3114
3120
  if (o) {
3115
- if (o[0] === 0 && o[1] === 0 && o[2] === 0 && o[3] === 0) return hit("unspecified", false);
3121
+ if (o[0] === 0 && o[1] === 0 && o[2] === 0 && o[3] === 0) return hit("unspecified", true);
3116
3122
  if (o[0] === 169 && o[1] === 254) return hit("link-local", false);
3117
3123
  if (o[0] >= 224 && o[0] <= 239) return hit("multicast", false);
3118
3124
  if (o[0] === 100 && o[1] >= 64 && o[1] <= 127) return hit("cgnat", true);
@@ -3124,7 +3130,7 @@ function classifySsrf(host) {
3124
3130
  }
3125
3131
  const g = expandIpv6(ip);
3126
3132
  if (!g) return null;
3127
- if (g.every((x) => x === 0)) return hit("unspecified", false);
3133
+ if (g.every((x) => x === 0)) return hit("unspecified", true);
3128
3134
  if ((g[0] & 65472) === 65152) return hit("link-local", false);
3129
3135
  if ((g[0] & 65280) === 65280) return hit("multicast", false);
3130
3136
  if (g.slice(0, 7).every((x) => x === 0) && g[7] === 1) return hit("private", true);
@@ -3137,7 +3143,7 @@ var TIER_REASON = {
3137
3143
  metadata: "a cloud instance-metadata endpoint, the classic credential-theft target",
3138
3144
  "link-local": "a link-local address",
3139
3145
  multicast: "a multicast address",
3140
- unspecified: "the unspecified address",
3146
+ unspecified: "the unspecified address, which reaches this host",
3141
3147
  cgnat: "a carrier-grade NAT address",
3142
3148
  private: "a loopback or private address"
3143
3149
  };
@@ -3148,7 +3154,7 @@ function ssrfFloor(tokens, opts = {}) {
3148
3154
  for (const { token, binary } of tokens) {
3149
3155
  const m = classifySsrf(token);
3150
3156
  if (!m) continue;
3151
- if (m.tier === "private" && !opts.ssrfStrict) continue;
3157
+ if (STRICT_TIERS.has(m.tier) && !opts.ssrfStrict) continue;
3152
3158
  if (m.overridable && m.normalized && exempt.has(m.normalized)) continue;
3153
3159
  return {
3154
3160
  ...m,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node9/proxy",
3
- "version": "2.9.2",
3
+ "version": "2.9.3",
4
4
  "description": "The Sudo Command for AI Agents. Execution Security for Claude Code, Codex, Gemini, Cursor, Opencode, Pi, and any MCP server.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",