@node9/policy-engine 2.9.1 → 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/index.d.mts CHANGED
@@ -1207,7 +1207,7 @@ declare const CANONICAL_EXTRACTOR_VERSION = "canonical-v10";
1207
1207
  * files changed, this hash must change too, and you must consciously
1208
1208
  * decide whether to bump CANONICAL_EXTRACTOR_VERSION."
1209
1209
  */
1210
- declare const CANONICAL_EXTRACTOR_HASH = "5c786cc174281e51";
1210
+ declare const CANONICAL_EXTRACTOR_HASH = "c4edee9dc99eb69a";
1211
1211
  declare function extractCanonicalFindings(call: ToolCallEntry, ctx: ExtractContext): CanonicalFinding[];
1212
1212
  declare function extractSessionLevelFindings(calls: ReadonlyArray<SessionToolCall>, ctx: SessionExtractContext): CanonicalFinding[];
1213
1213
  /**
package/dist/index.d.ts CHANGED
@@ -1207,7 +1207,7 @@ declare const CANONICAL_EXTRACTOR_VERSION = "canonical-v10";
1207
1207
  * files changed, this hash must change too, and you must consciously
1208
1208
  * decide whether to bump CANONICAL_EXTRACTOR_VERSION."
1209
1209
  */
1210
- declare const CANONICAL_EXTRACTOR_HASH = "5c786cc174281e51";
1210
+ declare const CANONICAL_EXTRACTOR_HASH = "c4edee9dc99eb69a";
1211
1211
  declare function extractCanonicalFindings(call: ToolCallEntry, ctx: ExtractContext): CanonicalFinding[];
1212
1212
  declare function extractSessionLevelFindings(calls: ReadonlyArray<SessionToolCall>, ctx: SessionExtractContext): CanonicalFinding[];
1213
1213
  /**
package/dist/index.js CHANGED
@@ -2866,9 +2866,15 @@ var METADATA_ADDRESSES = /* @__PURE__ */ new Set([
2866
2866
  // AWS ECS task role
2867
2867
  "168.63.129.16",
2868
2868
  // Azure WireServer
2869
- "fd00:ec2::254"
2869
+ "fd00:ec2::254",
2870
2870
  // AWS IMDS over IPv6 (inside fc00::/7, which is NOT a tier)
2871
+ // Alibaba Cloud IMDS. It sits INSIDE 100.64/10, so before this line it was
2872
+ // classified as cgnat and therefore exemptable: one ssrfAllow entry opened a
2873
+ // live credential endpoint. It has to be named here, above the range check,
2874
+ // and it is the reason relaxing cgnat is safe.
2875
+ "100.100.100.200"
2871
2876
  ]);
2877
+ var STRICT_TIERS = /* @__PURE__ */ new Set(["private", "unspecified", "cgnat"]);
2872
2878
  var METADATA_HOSTNAMES = /* @__PURE__ */ new Set(["metadata.google.internal", "metadata.goog", "metadata"]);
2873
2879
  var v4Octets = (a) => {
2874
2880
  const p = a.split(".");
@@ -2891,7 +2897,7 @@ function classifySsrf(host) {
2891
2897
  if (METADATA_ADDRESSES.has(ip)) return hit("metadata", false);
2892
2898
  const o = v4Octets(ip);
2893
2899
  if (o) {
2894
- if (o[0] === 0 && o[1] === 0 && o[2] === 0 && o[3] === 0) return hit("unspecified", false);
2900
+ if (o[0] === 0 && o[1] === 0 && o[2] === 0 && o[3] === 0) return hit("unspecified", true);
2895
2901
  if (o[0] === 169 && o[1] === 254) return hit("link-local", false);
2896
2902
  if (o[0] >= 224 && o[0] <= 239) return hit("multicast", false);
2897
2903
  if (o[0] === 100 && o[1] >= 64 && o[1] <= 127) return hit("cgnat", true);
@@ -2903,7 +2909,7 @@ function classifySsrf(host) {
2903
2909
  }
2904
2910
  const g = expandIpv6(ip);
2905
2911
  if (!g) return null;
2906
- if (g.every((x) => x === 0)) return hit("unspecified", false);
2912
+ if (g.every((x) => x === 0)) return hit("unspecified", true);
2907
2913
  if ((g[0] & 65472) === 65152) return hit("link-local", false);
2908
2914
  if ((g[0] & 65280) === 65280) return hit("multicast", false);
2909
2915
  if (g.slice(0, 7).every((x) => x === 0) && g[7] === 1) return hit("private", true);
@@ -2916,7 +2922,7 @@ var TIER_REASON = {
2916
2922
  metadata: "a cloud instance-metadata endpoint, the classic credential-theft target",
2917
2923
  "link-local": "a link-local address",
2918
2924
  multicast: "a multicast address",
2919
- unspecified: "the unspecified address",
2925
+ unspecified: "the unspecified address, which reaches this host",
2920
2926
  cgnat: "a carrier-grade NAT address",
2921
2927
  private: "a loopback or private address"
2922
2928
  };
@@ -2927,7 +2933,7 @@ function ssrfFloor(tokens, opts = {}) {
2927
2933
  for (const { token, binary } of tokens) {
2928
2934
  const m = classifySsrf(token);
2929
2935
  if (!m) continue;
2930
- if (m.tier === "private" && !opts.ssrfStrict) continue;
2936
+ if (STRICT_TIERS.has(m.tier) && !opts.ssrfStrict) continue;
2931
2937
  if (m.overridable && m.normalized && exempt.has(m.normalized)) continue;
2932
2938
  return {
2933
2939
  ...m,
@@ -4677,7 +4683,7 @@ function matchCanaryArgs(args, values) {
4677
4683
  // src/scan/canonical.ts
4678
4684
  var LONG_OUTPUT_THRESHOLD_BYTES = 100 * 1024;
4679
4685
  var CANONICAL_EXTRACTOR_VERSION = "canonical-v10";
4680
- var CANONICAL_EXTRACTOR_HASH = "5c786cc174281e51";
4686
+ var CANONICAL_EXTRACTOR_HASH = "c4edee9dc99eb69a";
4681
4687
  var DEDUPE_PREVIEW_LEN = 120;
4682
4688
  function extractCanonicalFindings(call, ctx) {
4683
4689
  const out = [];
package/dist/index.mjs CHANGED
@@ -2743,9 +2743,15 @@ var METADATA_ADDRESSES = /* @__PURE__ */ new Set([
2743
2743
  // AWS ECS task role
2744
2744
  "168.63.129.16",
2745
2745
  // Azure WireServer
2746
- "fd00:ec2::254"
2746
+ "fd00:ec2::254",
2747
2747
  // AWS IMDS over IPv6 (inside fc00::/7, which is NOT a tier)
2748
+ // Alibaba Cloud IMDS. It sits INSIDE 100.64/10, so before this line it was
2749
+ // classified as cgnat and therefore exemptable: one ssrfAllow entry opened a
2750
+ // live credential endpoint. It has to be named here, above the range check,
2751
+ // and it is the reason relaxing cgnat is safe.
2752
+ "100.100.100.200"
2748
2753
  ]);
2754
+ var STRICT_TIERS = /* @__PURE__ */ new Set(["private", "unspecified", "cgnat"]);
2749
2755
  var METADATA_HOSTNAMES = /* @__PURE__ */ new Set(["metadata.google.internal", "metadata.goog", "metadata"]);
2750
2756
  var v4Octets = (a) => {
2751
2757
  const p = a.split(".");
@@ -2768,7 +2774,7 @@ function classifySsrf(host) {
2768
2774
  if (METADATA_ADDRESSES.has(ip)) return hit("metadata", false);
2769
2775
  const o = v4Octets(ip);
2770
2776
  if (o) {
2771
- if (o[0] === 0 && o[1] === 0 && o[2] === 0 && o[3] === 0) return hit("unspecified", false);
2777
+ if (o[0] === 0 && o[1] === 0 && o[2] === 0 && o[3] === 0) return hit("unspecified", true);
2772
2778
  if (o[0] === 169 && o[1] === 254) return hit("link-local", false);
2773
2779
  if (o[0] >= 224 && o[0] <= 239) return hit("multicast", false);
2774
2780
  if (o[0] === 100 && o[1] >= 64 && o[1] <= 127) return hit("cgnat", true);
@@ -2780,7 +2786,7 @@ function classifySsrf(host) {
2780
2786
  }
2781
2787
  const g = expandIpv6(ip);
2782
2788
  if (!g) return null;
2783
- if (g.every((x) => x === 0)) return hit("unspecified", false);
2789
+ if (g.every((x) => x === 0)) return hit("unspecified", true);
2784
2790
  if ((g[0] & 65472) === 65152) return hit("link-local", false);
2785
2791
  if ((g[0] & 65280) === 65280) return hit("multicast", false);
2786
2792
  if (g.slice(0, 7).every((x) => x === 0) && g[7] === 1) return hit("private", true);
@@ -2793,7 +2799,7 @@ var TIER_REASON = {
2793
2799
  metadata: "a cloud instance-metadata endpoint, the classic credential-theft target",
2794
2800
  "link-local": "a link-local address",
2795
2801
  multicast: "a multicast address",
2796
- unspecified: "the unspecified address",
2802
+ unspecified: "the unspecified address, which reaches this host",
2797
2803
  cgnat: "a carrier-grade NAT address",
2798
2804
  private: "a loopback or private address"
2799
2805
  };
@@ -2804,7 +2810,7 @@ function ssrfFloor(tokens, opts = {}) {
2804
2810
  for (const { token, binary } of tokens) {
2805
2811
  const m = classifySsrf(token);
2806
2812
  if (!m) continue;
2807
- if (m.tier === "private" && !opts.ssrfStrict) continue;
2813
+ if (STRICT_TIERS.has(m.tier) && !opts.ssrfStrict) continue;
2808
2814
  if (m.overridable && m.normalized && exempt.has(m.normalized)) continue;
2809
2815
  return {
2810
2816
  ...m,
@@ -4554,7 +4560,7 @@ function matchCanaryArgs(args, values) {
4554
4560
  // src/scan/canonical.ts
4555
4561
  var LONG_OUTPUT_THRESHOLD_BYTES = 100 * 1024;
4556
4562
  var CANONICAL_EXTRACTOR_VERSION = "canonical-v10";
4557
- var CANONICAL_EXTRACTOR_HASH = "5c786cc174281e51";
4563
+ var CANONICAL_EXTRACTOR_HASH = "c4edee9dc99eb69a";
4558
4564
  var DEDUPE_PREVIEW_LEN = 120;
4559
4565
  function extractCanonicalFindings(call, ctx) {
4560
4566
  const out = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node9/policy-engine",
3
- "version": "2.9.1",
3
+ "version": "2.9.3",
4
4
  "description": "Shared policy evaluation engine for node9 — DLP, smart rules, AST shell parsing, shields, loop detection. Pure functions, no I/O. Used by both node9-proxy and the node9 SaaS firewall.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://node9.ai",
@@ -52,6 +52,6 @@
52
52
  "@types/picomatch": "^4.0.2",
53
53
  "tsup": "^8.0.0",
54
54
  "typescript": "^5.0.0",
55
- "vitest": "^4.0.0"
55
+ "vitest": "^5.0.0"
56
56
  }
57
57
  }