@actuate-media/cms-core 0.11.2 → 0.13.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.
Files changed (115) hide show
  1. package/dist/__tests__/api/cron-routes.test.d.ts +2 -0
  2. package/dist/__tests__/api/cron-routes.test.d.ts.map +1 -0
  3. package/dist/__tests__/api/cron-routes.test.js +67 -0
  4. package/dist/__tests__/api/cron-routes.test.js.map +1 -0
  5. package/dist/__tests__/api/health.test.d.ts +2 -0
  6. package/dist/__tests__/api/health.test.d.ts.map +1 -0
  7. package/dist/__tests__/api/health.test.js +140 -0
  8. package/dist/__tests__/api/health.test.js.map +1 -0
  9. package/dist/__tests__/auth/oauth.test.d.ts +2 -0
  10. package/dist/__tests__/auth/oauth.test.d.ts.map +1 -0
  11. package/dist/__tests__/auth/oauth.test.js +406 -0
  12. package/dist/__tests__/auth/oauth.test.js.map +1 -0
  13. package/dist/__tests__/auth/password.test.js +82 -3
  14. package/dist/__tests__/auth/password.test.js.map +1 -1
  15. package/dist/__tests__/auth/reset.test.d.ts +2 -0
  16. package/dist/__tests__/auth/reset.test.d.ts.map +1 -0
  17. package/dist/__tests__/auth/reset.test.js +303 -0
  18. package/dist/__tests__/auth/reset.test.js.map +1 -0
  19. package/dist/__tests__/auth/session.test.js +54 -1
  20. package/dist/__tests__/auth/session.test.js.map +1 -1
  21. package/dist/__tests__/cron/cron.test.d.ts +2 -0
  22. package/dist/__tests__/cron/cron.test.d.ts.map +1 -0
  23. package/dist/__tests__/cron/cron.test.js +262 -0
  24. package/dist/__tests__/cron/cron.test.js.map +1 -0
  25. package/dist/__tests__/diagnostics/env.test.d.ts +2 -0
  26. package/dist/__tests__/diagnostics/env.test.d.ts.map +1 -0
  27. package/dist/__tests__/diagnostics/env.test.js +119 -0
  28. package/dist/__tests__/diagnostics/env.test.js.map +1 -0
  29. package/dist/__tests__/diagnostics/logger.test.d.ts +2 -0
  30. package/dist/__tests__/diagnostics/logger.test.d.ts.map +1 -0
  31. package/dist/__tests__/diagnostics/logger.test.js +111 -0
  32. package/dist/__tests__/diagnostics/logger.test.js.map +1 -0
  33. package/dist/__tests__/security/encrypted-fields.test.d.ts +2 -0
  34. package/dist/__tests__/security/encrypted-fields.test.d.ts.map +1 -0
  35. package/dist/__tests__/security/encrypted-fields.test.js +60 -0
  36. package/dist/__tests__/security/encrypted-fields.test.js.map +1 -0
  37. package/dist/__tests__/security/rate-limit.test.js +42 -0
  38. package/dist/__tests__/security/rate-limit.test.js.map +1 -1
  39. package/dist/__tests__/security/safe-fetch.test.d.ts +2 -0
  40. package/dist/__tests__/security/safe-fetch.test.d.ts.map +1 -0
  41. package/dist/__tests__/security/safe-fetch.test.js +97 -0
  42. package/dist/__tests__/security/safe-fetch.test.js.map +1 -0
  43. package/dist/__tests__/security/ssrf.test.d.ts +2 -0
  44. package/dist/__tests__/security/ssrf.test.d.ts.map +1 -0
  45. package/dist/__tests__/security/ssrf.test.js +209 -0
  46. package/dist/__tests__/security/ssrf.test.js.map +1 -0
  47. package/dist/actions.d.ts.map +1 -1
  48. package/dist/actions.js +7 -6
  49. package/dist/actions.js.map +1 -1
  50. package/dist/api/handler-factory.d.ts.map +1 -1
  51. package/dist/api/handler-factory.js +15 -6
  52. package/dist/api/handler-factory.js.map +1 -1
  53. package/dist/api/handlers.d.ts.map +1 -1
  54. package/dist/api/handlers.js +165 -26
  55. package/dist/api/handlers.js.map +1 -1
  56. package/dist/auth/oauth.d.ts +8 -0
  57. package/dist/auth/oauth.d.ts.map +1 -1
  58. package/dist/auth/oauth.js +44 -2
  59. package/dist/auth/oauth.js.map +1 -1
  60. package/dist/auth/password.d.ts +35 -2
  61. package/dist/auth/password.d.ts.map +1 -1
  62. package/dist/auth/password.js +97 -7
  63. package/dist/auth/password.js.map +1 -1
  64. package/dist/auth/reset.d.ts.map +1 -1
  65. package/dist/auth/reset.js +2 -1
  66. package/dist/auth/reset.js.map +1 -1
  67. package/dist/auth/session.d.ts +9 -0
  68. package/dist/auth/session.d.ts.map +1 -1
  69. package/dist/auth/session.js +54 -1
  70. package/dist/auth/session.js.map +1 -1
  71. package/dist/config/runtime.d.ts +99 -0
  72. package/dist/config/runtime.d.ts.map +1 -0
  73. package/dist/config/runtime.js +43 -0
  74. package/dist/config/runtime.js.map +1 -0
  75. package/dist/config/types.d.ts +21 -0
  76. package/dist/config/types.d.ts.map +1 -1
  77. package/dist/cron/index.d.ts +72 -0
  78. package/dist/cron/index.d.ts.map +1 -0
  79. package/dist/cron/index.js +222 -0
  80. package/dist/cron/index.js.map +1 -0
  81. package/dist/diagnostics/env.d.ts +44 -0
  82. package/dist/diagnostics/env.d.ts.map +1 -0
  83. package/dist/diagnostics/env.js +293 -0
  84. package/dist/diagnostics/env.js.map +1 -0
  85. package/dist/diagnostics/logger.d.ts +38 -0
  86. package/dist/diagnostics/logger.d.ts.map +1 -0
  87. package/dist/diagnostics/logger.js +89 -0
  88. package/dist/diagnostics/logger.js.map +1 -0
  89. package/dist/page-builder/blocks.d.ts.map +1 -1
  90. package/dist/page-builder/blocks.js +6 -1
  91. package/dist/page-builder/blocks.js.map +1 -1
  92. package/dist/security/audit.d.ts.map +1 -1
  93. package/dist/security/audit.js +3 -1
  94. package/dist/security/audit.js.map +1 -1
  95. package/dist/security/encrypted-fields.d.ts +9 -0
  96. package/dist/security/encrypted-fields.d.ts.map +1 -1
  97. package/dist/security/encrypted-fields.js +52 -1
  98. package/dist/security/encrypted-fields.js.map +1 -1
  99. package/dist/security/ip-canon.d.ts +71 -0
  100. package/dist/security/ip-canon.d.ts.map +1 -0
  101. package/dist/security/ip-canon.js +352 -0
  102. package/dist/security/ip-canon.js.map +1 -0
  103. package/dist/security/rate-limit.d.ts +8 -0
  104. package/dist/security/rate-limit.d.ts.map +1 -1
  105. package/dist/security/rate-limit.js +81 -3
  106. package/dist/security/rate-limit.js.map +1 -1
  107. package/dist/security/safe-fetch.d.ts +30 -8
  108. package/dist/security/safe-fetch.d.ts.map +1 -1
  109. package/dist/security/safe-fetch.js +32 -6
  110. package/dist/security/safe-fetch.js.map +1 -1
  111. package/dist/security/webhook.d.ts +20 -2
  112. package/dist/security/webhook.d.ts.map +1 -1
  113. package/dist/security/webhook.js +100 -30
  114. package/dist/security/webhook.js.map +1 -1
  115. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"encrypted-fields.js","sourceRoot":"","sources":["../../src/security/encrypted-fields.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"encrypted-fields.js","sourceRoot":"","sources":["../../src/security/encrypted-fields.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * IP canonicalization for SSRF defense.
3
+ *
4
+ * The previous regex-based approach in `webhook.ts` only matched the literal
5
+ * dotted-quad form. It missed every encoding that `getaddrinfo` (and therefore
6
+ * `fetch`) accepts:
7
+ *
8
+ * - Decimal: http://2130706433/ → 127.0.0.1
9
+ * - Octal: http://0177.0.0.1/ → 127.0.0.1
10
+ * - Hex: http://0x7f.0.0.1/ → 127.0.0.1
11
+ * - Short-form: http://127.1/ → 127.0.0.1
12
+ * - IPv4-mapped v6: http://[::ffff:127.0.0.1] → 127.0.0.1
13
+ * - IPv4-compat v6: http://[::127.0.0.1] → 127.0.0.1
14
+ *
15
+ * This module canonicalizes any of those forms to a 4-octet IPv4 string (or
16
+ * a normalized 16-byte IPv6 address), then range-checks against a complete
17
+ * private/internal block list including AWS/GCP/Azure metadata endpoints and
18
+ * the carrier-grade NAT range.
19
+ */
20
+ /**
21
+ * Result of {@link canonicalizeHostname}. There are exactly three valid
22
+ * shapes:
23
+ *
24
+ * - **Valid IPv4 literal** — `isHostname=false`, `isValidIp=true`, `ipv4` set.
25
+ * - **Valid IPv6 literal** — `isHostname=false`, `isValidIp=true`, `ipv6` set.
26
+ * - **Hostname (DNS name)** — `isHostname=true`, `isValidIp=false`, neither
27
+ * `ipv4` nor `ipv6` set.
28
+ * - **Malformed IP literal** — `isHostname=false`, `isValidIp=false`, neither
29
+ * IP field set. (Inputs that look like IP literals — e.g. contain `:` or
30
+ * only digits and dots — but fail to parse cleanly.)
31
+ *
32
+ * SSRF gates MUST treat the malformed shape as "reject" rather than as
33
+ * "validated IP literal". Use `isValidIp` to distinguish the two `false`
34
+ * `isHostname` cases.
35
+ */
36
+ export interface CanonicalizedHost {
37
+ /** Original hostname as it appeared in the URL. */
38
+ raw: string;
39
+ /** Canonical IPv4 dotted-quad string when the literal was IPv4 (in any form). */
40
+ ipv4?: string;
41
+ /** Canonical IPv6 hextet string when the literal was IPv6. */
42
+ ipv6?: string;
43
+ /** True when the input was a DNS name (not an IP literal in any form). */
44
+ isHostname: boolean;
45
+ /**
46
+ * True when the input parsed cleanly into a canonical IP address (`ipv4`
47
+ * or `ipv6` is set). False for both DNS hostnames and malformed IP-shaped
48
+ * strings. Callers performing a security check on the parsed IP MUST
49
+ * verify `isValidIp` before reading `ipv4` / `ipv6`.
50
+ */
51
+ isValidIp: boolean;
52
+ }
53
+ /**
54
+ * Canonicalize a URL hostname into either a normalized IP address or a
55
+ * passthrough hostname. Handles every IPv4 encoding accepted by `inet_aton`
56
+ * (decimal, octal, hex, short-form), IPv4-mapped IPv6, and bracketed IPv6.
57
+ */
58
+ export declare function canonicalizeHostname(rawHostname: string): CanonicalizedHost;
59
+ /**
60
+ * Returns the matching private/internal range when the canonicalized host is
61
+ * one, `null` when it's a safe public IP, or a "malformed" reason when the
62
+ * input claims to be an IP literal but didn't parse cleanly.
63
+ *
64
+ * Defense-in-depth: even if a caller forgets to gate on `isValidIp`, this
65
+ * function still fails closed on the malformed shape (`isHostname=false`
66
+ * with no `ipv4`/`ipv6`).
67
+ */
68
+ export declare function isPrivateAddress(host: CanonicalizedHost): {
69
+ reason: string;
70
+ } | null;
71
+ //# sourceMappingURL=ip-canon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ip-canon.d.ts","sourceRoot":"","sources":["../../src/security/ip-canon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAiCH;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,iBAAiB;IAChC,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAA;IACX,iFAAiF;IACjF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,0EAA0E;IAC1E,UAAU,EAAE,OAAO,CAAA;IACnB;;;;;OAKG;IACH,SAAS,EAAE,OAAO,CAAA;CACnB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,iBAAiB,CAmC3E;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,iBAAiB,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA4BnF"}
@@ -0,0 +1,352 @@
1
+ /**
2
+ * IP canonicalization for SSRF defense.
3
+ *
4
+ * The previous regex-based approach in `webhook.ts` only matched the literal
5
+ * dotted-quad form. It missed every encoding that `getaddrinfo` (and therefore
6
+ * `fetch`) accepts:
7
+ *
8
+ * - Decimal: http://2130706433/ → 127.0.0.1
9
+ * - Octal: http://0177.0.0.1/ → 127.0.0.1
10
+ * - Hex: http://0x7f.0.0.1/ → 127.0.0.1
11
+ * - Short-form: http://127.1/ → 127.0.0.1
12
+ * - IPv4-mapped v6: http://[::ffff:127.0.0.1] → 127.0.0.1
13
+ * - IPv4-compat v6: http://[::127.0.0.1] → 127.0.0.1
14
+ *
15
+ * This module canonicalizes any of those forms to a 4-octet IPv4 string (or
16
+ * a normalized 16-byte IPv6 address), then range-checks against a complete
17
+ * private/internal block list including AWS/GCP/Azure metadata endpoints and
18
+ * the carrier-grade NAT range.
19
+ */
20
+ const PRIVATE_IPV4_RANGES = [
21
+ // 0.0.0.0/8 "this network"
22
+ { network: 0x00_00_00_00, mask: 0xff_00_00_00, reason: '0.0.0.0/8 (this network)' },
23
+ // 10.0.0.0/8 RFC1918 private
24
+ { network: 0x0a_00_00_00, mask: 0xff_00_00_00, reason: '10.0.0.0/8 (RFC1918 private)' },
25
+ // 100.64.0.0/10 RFC6598 carrier-grade NAT
26
+ { network: 0x64_40_00_00, mask: 0xff_c0_00_00, reason: '100.64.0.0/10 (CGNAT)' },
27
+ // 127.0.0.0/8 loopback
28
+ { network: 0x7f_00_00_00, mask: 0xff_00_00_00, reason: '127.0.0.0/8 (loopback)' },
29
+ // 169.254.0.0/16 link-local + cloud metadata (AWS 169.254.169.254 is in here)
30
+ { network: 0xa9_fe_00_00, mask: 0xff_ff_00_00, reason: '169.254.0.0/16 (link-local + metadata)' },
31
+ // 172.16.0.0/12 RFC1918 private
32
+ { network: 0xac_10_00_00, mask: 0xff_f0_00_00, reason: '172.16.0.0/12 (RFC1918 private)' },
33
+ // 192.0.0.0/24 RFC6890 IETF protocol assignments
34
+ { network: 0xc0_00_00_00, mask: 0xff_ff_ff_00, reason: '192.0.0.0/24 (IETF reserved)' },
35
+ // 192.0.2.0/24 TEST-NET-1 (documentation)
36
+ { network: 0xc0_00_02_00, mask: 0xff_ff_ff_00, reason: '192.0.2.0/24 (TEST-NET-1)' },
37
+ // 192.168.0.0/16 RFC1918 private
38
+ { network: 0xc0_a8_00_00, mask: 0xff_ff_00_00, reason: '192.168.0.0/16 (RFC1918 private)' },
39
+ // 198.18.0.0/15 benchmark
40
+ { network: 0xc6_12_00_00, mask: 0xff_fe_00_00, reason: '198.18.0.0/15 (benchmark)' },
41
+ // 198.51.100.0/24 TEST-NET-2
42
+ { network: 0xc6_33_64_00, mask: 0xff_ff_ff_00, reason: '198.51.100.0/24 (TEST-NET-2)' },
43
+ // 203.0.113.0/24 TEST-NET-3
44
+ { network: 0xcb_00_71_00, mask: 0xff_ff_ff_00, reason: '203.0.113.0/24 (TEST-NET-3)' },
45
+ // 224.0.0.0/4 multicast
46
+ { network: 0xe0_00_00_00, mask: 0xf0_00_00_00, reason: '224.0.0.0/4 (multicast)' },
47
+ // 240.0.0.0/4 reserved (includes 255.255.255.255 broadcast)
48
+ { network: 0xf0_00_00_00, mask: 0xf0_00_00_00, reason: '240.0.0.0/4 (reserved)' },
49
+ ];
50
+ /**
51
+ * Canonicalize a URL hostname into either a normalized IP address or a
52
+ * passthrough hostname. Handles every IPv4 encoding accepted by `inet_aton`
53
+ * (decimal, octal, hex, short-form), IPv4-mapped IPv6, and bracketed IPv6.
54
+ */
55
+ export function canonicalizeHostname(rawHostname) {
56
+ const raw = rawHostname;
57
+ // Strip surrounding brackets (URLs use `[::1]` for IPv6 hosts).
58
+ let host = raw;
59
+ if (host.startsWith('[') && host.endsWith(']')) {
60
+ host = host.slice(1, -1);
61
+ }
62
+ // Try IPv6 first — only IPv6 contains ':'.
63
+ if (host.includes(':')) {
64
+ const ipv6 = parseIPv6(host);
65
+ if (ipv6) {
66
+ // IPv4-mapped (::ffff:a.b.c.d) and IPv4-compatible (::a.b.c.d) both
67
+ // tunnel an IPv4 address through IPv6 — collapse to the underlying v4
68
+ // so the v4 range check catches them.
69
+ const tunnelled = extractIPv4FromIPv6(ipv6);
70
+ if (tunnelled !== null) {
71
+ return { raw, ipv4: ipv4ToString(tunnelled), isHostname: false, isValidIp: true };
72
+ }
73
+ return { raw, ipv6: formatIPv6(ipv6), isHostname: false, isValidIp: true };
74
+ }
75
+ // Looked like IPv6 (had a colon) but didn't parse. Mark as a non-hostname
76
+ // *and* not a valid IP — SSRF gates fail closed on this shape.
77
+ return { raw, isHostname: false, isValidIp: false };
78
+ }
79
+ // Try IPv4 in any of: decimal, octal, hex, dotted, short-form.
80
+ const ipv4 = parseIPv4Literal(host);
81
+ if (ipv4 !== null) {
82
+ return { raw, ipv4: ipv4ToString(ipv4), isHostname: false, isValidIp: true };
83
+ }
84
+ // Not an IP literal in any form → treat as DNS hostname.
85
+ return { raw, isHostname: true, isValidIp: false };
86
+ }
87
+ /**
88
+ * Returns the matching private/internal range when the canonicalized host is
89
+ * one, `null` when it's a safe public IP, or a "malformed" reason when the
90
+ * input claims to be an IP literal but didn't parse cleanly.
91
+ *
92
+ * Defense-in-depth: even if a caller forgets to gate on `isValidIp`, this
93
+ * function still fails closed on the malformed shape (`isHostname=false`
94
+ * with no `ipv4`/`ipv6`).
95
+ */
96
+ export function isPrivateAddress(host) {
97
+ if (host.ipv4) {
98
+ const num = ipv4StringToNumber(host.ipv4);
99
+ if (num === null)
100
+ return { reason: 'invalid IPv4 numeric form' };
101
+ for (const range of PRIVATE_IPV4_RANGES) {
102
+ // `>>> 0` forces the int32 result of `&` back to an unsigned 32-bit
103
+ // value so comparisons with `range.network` (a positive JS number)
104
+ // work correctly even when the high bit is set (172.16.x.x and up).
105
+ if ((num & range.mask) >>> 0 === range.network) {
106
+ return { reason: range.reason };
107
+ }
108
+ }
109
+ return null;
110
+ }
111
+ if (host.ipv6) {
112
+ const v6 = parseIPv6(host.ipv6);
113
+ if (!v6)
114
+ return { reason: 'invalid IPv6 form' };
115
+ return checkPrivateIPv6(v6);
116
+ }
117
+ // Malformed IP literal (looked like an IP but didn't parse). Fail closed.
118
+ if (!host.isHostname) {
119
+ return { reason: 'malformed IP literal' };
120
+ }
121
+ // DNS hostname → no IP-level check possible without resolution.
122
+ return null;
123
+ }
124
+ /**
125
+ * Parse an IPv4 literal in *any* form `inet_aton` accepts:
126
+ * - 4 parts: a.b.c.d (each 0-255)
127
+ * - 3 parts: a.b.c (c is bottom 16 bits)
128
+ * - 2 parts: a.b (b is bottom 24 bits)
129
+ * - 1 part: a (full 32-bit number)
130
+ *
131
+ * Each part may be decimal, octal (leading 0), or hex (leading 0x).
132
+ */
133
+ function parseIPv4Literal(input) {
134
+ if (input.length === 0)
135
+ return null;
136
+ const parts = input.split('.');
137
+ if (parts.length === 0 || parts.length > 4)
138
+ return null;
139
+ const nums = [];
140
+ for (const p of parts) {
141
+ if (p.length === 0)
142
+ return null;
143
+ const n = parsePartNumber(p);
144
+ if (n === null)
145
+ return null;
146
+ nums.push(n);
147
+ }
148
+ // Build 32-bit value per inet_aton rules.
149
+ switch (nums.length) {
150
+ case 1: {
151
+ const a = nums[0];
152
+ if (a > 0xff_ff_ff_ff)
153
+ return null;
154
+ return a >>> 0;
155
+ }
156
+ case 2: {
157
+ const [a, b] = nums;
158
+ if (a > 0xff || b > 0xff_ff_ff)
159
+ return null;
160
+ return ((a << 24) | b) >>> 0;
161
+ }
162
+ case 3: {
163
+ const [a, b, c] = nums;
164
+ if (a > 0xff || b > 0xff || c > 0xff_ff)
165
+ return null;
166
+ return ((a << 24) | (b << 16) | c) >>> 0;
167
+ }
168
+ case 4: {
169
+ const [a, b, c, d] = nums;
170
+ if (a > 0xff || b > 0xff || c > 0xff || d > 0xff)
171
+ return null;
172
+ return ((a << 24) | (b << 16) | (c << 8) | d) >>> 0;
173
+ }
174
+ default:
175
+ return null;
176
+ }
177
+ }
178
+ function parsePartNumber(part) {
179
+ // Hex: 0x...
180
+ if (part.startsWith('0x') || part.startsWith('0X')) {
181
+ const hex = part.slice(2);
182
+ if (hex.length === 0 || !/^[0-9a-fA-F]+$/.test(hex))
183
+ return null;
184
+ const n = parseInt(hex, 16);
185
+ return Number.isFinite(n) ? n : null;
186
+ }
187
+ // Octal: starts with 0 followed by digits (but bare "0" is decimal 0).
188
+ if (part.length > 1 && part.startsWith('0')) {
189
+ if (!/^[0-7]+$/.test(part))
190
+ return null;
191
+ const n = parseInt(part, 8);
192
+ return Number.isFinite(n) ? n : null;
193
+ }
194
+ // Decimal.
195
+ if (!/^[0-9]+$/.test(part))
196
+ return null;
197
+ const n = parseInt(part, 10);
198
+ return Number.isFinite(n) ? n : null;
199
+ }
200
+ function ipv4StringToNumber(s) {
201
+ const parts = s.split('.');
202
+ if (parts.length !== 4)
203
+ return null;
204
+ let n = 0;
205
+ for (const p of parts) {
206
+ const x = parseInt(p, 10);
207
+ if (!Number.isFinite(x) || x < 0 || x > 255)
208
+ return null;
209
+ n = (n << 8) | x;
210
+ }
211
+ return n >>> 0;
212
+ }
213
+ function ipv4ToString(n) {
214
+ return [(n >>> 24) & 0xff, (n >>> 16) & 0xff, (n >>> 8) & 0xff, n & 0xff].join('.');
215
+ }
216
+ /**
217
+ * Parse an IPv6 address into 8 groups of 16-bit numbers. Supports `::`
218
+ * compression and the `::ffff:a.b.c.d` IPv4-mapped trailing form.
219
+ *
220
+ * Reconstruction order with compression:
221
+ * [head hextets] [zero-fill] [tail hextets] [optional v4 suffix as 2 hextets]
222
+ *
223
+ * Without compression: just `[8 hextets, optional last replaced by v4 suffix]`.
224
+ */
225
+ function parseIPv6(input) {
226
+ if (input.length === 0)
227
+ return null;
228
+ const doubleColon = input.indexOf('::');
229
+ let head;
230
+ let tail;
231
+ if (doubleColon >= 0) {
232
+ if (input.indexOf('::', doubleColon + 1) !== -1)
233
+ return null; // multiple ::
234
+ head = input
235
+ .slice(0, doubleColon)
236
+ .split(':')
237
+ .filter((s) => s.length > 0);
238
+ tail = input
239
+ .slice(doubleColon + 2)
240
+ .split(':')
241
+ .filter((s) => s.length > 0);
242
+ }
243
+ else {
244
+ head = input.split(':');
245
+ tail = [];
246
+ // No compression: must be exactly 8 hextets (or 7 + a trailing IPv4).
247
+ }
248
+ // The trailing group may be a dotted-quad IPv4. It's always at the end of
249
+ // either `tail` (when compression is present) or `head` (when not).
250
+ let v4Suffix = null;
251
+ const trailingGroupArr = tail.length > 0 ? tail : head;
252
+ const trailingGroup = trailingGroupArr[trailingGroupArr.length - 1] ?? '';
253
+ if (trailingGroup.includes('.')) {
254
+ const v4 = parseIPv4Literal(trailingGroup);
255
+ if (v4 === null)
256
+ return null;
257
+ v4Suffix = [(v4 >>> 16) & 0xff_ff, v4 & 0xff_ff];
258
+ if (tail.length > 0)
259
+ tail = tail.slice(0, -1);
260
+ else
261
+ head = head.slice(0, -1);
262
+ }
263
+ const headGroups = head.map(parseHextet);
264
+ const tailGroups = tail.map(parseHextet);
265
+ if (headGroups.includes(null) || tailGroups.includes(null))
266
+ return null;
267
+ const v4Count = v4Suffix ? 2 : 0;
268
+ if (doubleColon >= 0) {
269
+ const fillCount = 8 - headGroups.length - tailGroups.length - v4Count;
270
+ if (fillCount < 0)
271
+ return null;
272
+ const zeros = new Array(fillCount).fill(0);
273
+ const result = [
274
+ ...headGroups,
275
+ ...zeros,
276
+ ...tailGroups,
277
+ ...(v4Suffix ?? []),
278
+ ];
279
+ return result.length === 8 ? result : null;
280
+ }
281
+ // No compression — must add up to exactly 8 hextets including v4 suffix.
282
+ const result = [...headGroups, ...(v4Suffix ?? [])];
283
+ return result.length === 8 ? result : null;
284
+ }
285
+ function parseHextet(s) {
286
+ if (s.length === 0 || s.length > 4)
287
+ return null;
288
+ if (!/^[0-9a-fA-F]+$/.test(s))
289
+ return null;
290
+ return parseInt(s, 16);
291
+ }
292
+ function formatIPv6(groups) {
293
+ return groups.map((g) => g.toString(16)).join(':');
294
+ }
295
+ /**
296
+ * If `groups` represents an IPv4-mapped (`::ffff:a.b.c.d`) or non-trivial
297
+ * IPv4-compatible (`::a.b.c.d` with a.b.c.d != ::1, deprecated) IPv6 address,
298
+ * return the inner 32-bit IPv4 value.
299
+ *
300
+ * IMPORTANT: We deliberately do NOT extract from `::1` (loopback) or `::`
301
+ * (unspecified). Those are pure IPv6 addresses, not tunneled IPv4 — treating
302
+ * `::1` as `0.0.0.1` would silently allow loopback bypass via the IPv4
303
+ * range checks (which don't include `0.0.0.1`).
304
+ */
305
+ function extractIPv4FromIPv6(groups) {
306
+ // First 5 groups must be all zero (the high-order 80 bits).
307
+ if (groups.slice(0, 5).some((g) => g !== 0))
308
+ return null;
309
+ const isMapped = groups[5] === 0xff_ff;
310
+ if (isMapped) {
311
+ return (((groups[6] ?? 0) << 16) | (groups[7] ?? 0)) >>> 0;
312
+ }
313
+ // IPv4-compatible (deprecated by RFC4291 §2.5.5.1): only when groups[5]===0
314
+ // AND the embedded "IPv4" is itself a real public-looking address (i.e.
315
+ // the whole thing isn't just `::N` for small N). We require the high
316
+ // hextet of the IPv4 portion (groups[6]) to be non-zero — that excludes
317
+ // `::1`, `::`, `::N` for N < 65536, all of which should be treated as
318
+ // pure IPv6 by their own range checks (loopback, unspecified, etc).
319
+ const isCompat = groups[5] === 0 && (groups[6] ?? 0) !== 0;
320
+ if (isCompat) {
321
+ return (((groups[6] ?? 0) << 16) | (groups[7] ?? 0)) >>> 0;
322
+ }
323
+ return null;
324
+ }
325
+ function checkPrivateIPv6(groups) {
326
+ // ::1 loopback
327
+ if (groups.every((g, i) => (i === 7 ? g === 1 : g === 0))) {
328
+ return { reason: '::1 (IPv6 loopback)' };
329
+ }
330
+ // :: unspecified
331
+ if (groups.every((g) => g === 0)) {
332
+ return { reason: ':: (unspecified)' };
333
+ }
334
+ // fc00::/7 unique local
335
+ if ((groups[0] & 0xfe_00) === 0xfc_00) {
336
+ return { reason: 'fc00::/7 (IPv6 unique local)' };
337
+ }
338
+ // fe80::/10 link-local
339
+ if ((groups[0] & 0xff_c0) === 0xfe_80) {
340
+ return { reason: 'fe80::/10 (IPv6 link-local)' };
341
+ }
342
+ // ff00::/8 multicast
343
+ if ((groups[0] & 0xff_00) === 0xff_00) {
344
+ return { reason: 'ff00::/8 (IPv6 multicast)' };
345
+ }
346
+ // 64:ff9b::/96 well-known IPv4/IPv6 translation
347
+ if (groups[0] === 0x00_64 && groups[1] === 0xff_9b && groups.slice(2, 6).every((g) => g === 0)) {
348
+ return { reason: '64:ff9b::/96 (IPv4-IPv6 translation)' };
349
+ }
350
+ return null;
351
+ }
352
+ //# sourceMappingURL=ip-canon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ip-canon.js","sourceRoot":"","sources":["../../src/security/ip-canon.ts"],"names":[],"mappings":"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"}
@@ -11,6 +11,14 @@ export interface RateLimiter {
11
11
  export interface RateLimitConfig {
12
12
  windowMs: number;
13
13
  maxRequests: number;
14
+ /**
15
+ * Maximum number of distinct keys retained by the in-memory limiter.
16
+ * When exceeded, the oldest entry (insertion order) is evicted. Defaults
17
+ * to 10,000 — high enough to cover any legitimate single-process traffic
18
+ * while bounding worst-case memory under enumeration / IP-spoofing
19
+ * attacks. See M1 in the engineering audit.
20
+ */
21
+ maxKeys?: number;
14
22
  }
15
23
  /** Create a rate limiter backed by an in-memory sliding window (for dev/single-process). */
16
24
  export declare function createInMemoryRateLimiter(config: RateLimitConfig): RateLimiter;
@@ -1 +1 @@
1
- {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/security/rate-limit.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,IAAI,CAAA;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAA;IAC5C,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAClC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,4FAA4F;AAC5F,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,eAAe,GAAG,WAAW,CA4B9E;AAED,iFAAiF;AACjF,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,eAAe,GAAG,WAAW,CAqF7E;AA6BD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,WAAW,CAkBtE"}
1
+ {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/security/rate-limit.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,IAAI,CAAA;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAA;IAC5C,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAClC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AASD,4FAA4F;AAC5F,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,eAAe,GAAG,WAAW,CAmE9E;AA2BD,iFAAiF;AACjF,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,eAAe,GAAG,WAAW,CAuF7E;AA6BD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,WAAW,CAkBtE"}
@@ -1,16 +1,61 @@
1
+ import { createLogger } from '../diagnostics/logger.js';
2
+ const logger = createLogger('rate-limit');
3
+ const DEFAULT_MAX_KEYS = 10_000;
4
+ /**
5
+ * Sweep expired entries on every Nth `check()` call. Tunable at module level
6
+ * so tests can stub it without exposing it via the public config surface.
7
+ */
8
+ const SWEEP_EVERY_N_CHECKS = 256;
1
9
  /** Create a rate limiter backed by an in-memory sliding window (for dev/single-process). */
2
10
  export function createInMemoryRateLimiter(config) {
11
+ // `Map` preserves insertion order so we can use it as an LRU directly:
12
+ // every `check()` re-inserts the touched key (delete + set) which moves it
13
+ // to the tail. When we exceed the cap, `windows.keys().next().value` is the
14
+ // least-recently-touched entry and we evict it.
3
15
  const windows = new Map();
16
+ const maxKeys = config.maxKeys && config.maxKeys > 0 ? config.maxKeys : DEFAULT_MAX_KEYS;
17
+ let checksSinceSweep = 0;
18
+ function sweepExpired(now) {
19
+ // Bounded periodic cleanup of expired entries. We cap iterations so a
20
+ // pathological burst of new keys can't make this O(n) on every call.
21
+ let removed = 0;
22
+ for (const [key, entry] of windows) {
23
+ if (entry.resetAt <= now) {
24
+ windows.delete(key);
25
+ removed++;
26
+ if (removed >= 1000)
27
+ break;
28
+ }
29
+ }
30
+ }
31
+ function evictIfFull() {
32
+ while (windows.size >= maxKeys) {
33
+ const oldestKey = windows.keys().next().value;
34
+ if (oldestKey === undefined)
35
+ break;
36
+ windows.delete(oldestKey);
37
+ }
38
+ }
4
39
  return {
5
40
  async check(key) {
6
41
  const now = Date.now();
42
+ checksSinceSweep++;
43
+ if (checksSinceSweep >= SWEEP_EVERY_N_CHECKS) {
44
+ checksSinceSweep = 0;
45
+ sweepExpired(now);
46
+ }
7
47
  const entry = windows.get(key);
8
48
  if (!entry || now > entry.resetAt) {
9
49
  const resetAt = now + config.windowMs;
50
+ windows.delete(key);
51
+ evictIfFull();
10
52
  windows.set(key, { count: 1, resetAt });
11
53
  return { allowed: true, remaining: config.maxRequests - 1, resetAt: new Date(resetAt) };
12
54
  }
13
55
  entry.count++;
56
+ // Re-insert to mark as most-recently-used.
57
+ windows.delete(key);
58
+ windows.set(key, entry);
14
59
  const allowed = entry.count <= config.maxRequests;
15
60
  return {
16
61
  allowed,
@@ -24,6 +69,31 @@ export function createInMemoryRateLimiter(config) {
24
69
  },
25
70
  };
26
71
  }
72
+ /**
73
+ * Throttle the noisy "Upstash failed" audit event so a sustained outage does
74
+ * not generate one DB write per request. We allow at most one audit log per
75
+ * `AUDIT_THROTTLE_MS`. The `console.error` line still fires on every failure
76
+ * for live operator visibility.
77
+ */
78
+ const AUDIT_THROTTLE_MS = 60_000;
79
+ let lastAuditAt = 0;
80
+ async function emitDegradedAudit(reason) {
81
+ const now = Date.now();
82
+ if (now - lastAuditAt < AUDIT_THROTTLE_MS)
83
+ return;
84
+ lastAuditAt = now;
85
+ try {
86
+ // Lazy import — avoids a static dep cycle (audit.ts -> db.js -> ...).
87
+ const { logEvent } = await import('./audit.js');
88
+ await logEvent({
89
+ event: 'rate_limit.degraded',
90
+ details: { reason, throttleMs: AUDIT_THROTTLE_MS },
91
+ });
92
+ }
93
+ catch {
94
+ // Audit failures are themselves logged in audit.ts; don't double-log here.
95
+ }
96
+ }
27
97
  /** Create a rate limiter backed by Upstash Redis (for serverless/production). */
28
98
  export function createUpstashRateLimiter(config) {
29
99
  const url = process.env.UPSTASH_REDIS_REST_URL;
@@ -79,8 +149,14 @@ export function createUpstashRateLimiter(config) {
79
149
  }
80
150
  catch (err) {
81
151
  // Fail open with logging — a Redis outage should NOT take the entire
82
- // CMS offline. Audit-level logging makes the degradation visible.
83
- console.error('[actuate][rate-limit] Upstash request failed, allowing through:', err instanceof Error ? err.message : err);
152
+ // CMS offline. We surface the degradation through:
153
+ // 1. console.error for live ops/log aggregator dashboards;
154
+ // 2. a throttled audit-log event so the outage is preserved in the
155
+ // durable security record without flooding the DB during long
156
+ // Upstash incidents.
157
+ const reason = err instanceof Error ? err.message : String(err);
158
+ logger.error('Upstash request failed, allowing through', { reason });
159
+ void emitDegradedAudit(reason);
84
160
  return {
85
161
  allowed: true,
86
162
  remaining: Math.max(0, config.maxRequests - 1),
@@ -93,7 +169,9 @@ export function createUpstashRateLimiter(config) {
93
169
  await redisPipeline([['DEL', `ratelimit:${key}`]]);
94
170
  }
95
171
  catch (err) {
96
- console.error('[actuate][rate-limit] Upstash reset failed:', err instanceof Error ? err.message : err);
172
+ logger.error('Upstash reset failed', {
173
+ reason: err instanceof Error ? err.message : String(err),
174
+ });
97
175
  }
98
176
  },
99
177
  };
@@ -1 +1 @@
1
- {"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../../src/security/rate-limit.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../../src/security/rate-limit.ts"],"names":[],"mappings":"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"}