@actuate-media/cms-core 0.11.2 → 0.12.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 (63) 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__/auth/password.test.js +82 -3
  6. package/dist/__tests__/auth/password.test.js.map +1 -1
  7. package/dist/__tests__/auth/session.test.js +54 -1
  8. package/dist/__tests__/auth/session.test.js.map +1 -1
  9. package/dist/__tests__/cron/cron.test.d.ts +2 -0
  10. package/dist/__tests__/cron/cron.test.d.ts.map +1 -0
  11. package/dist/__tests__/cron/cron.test.js +262 -0
  12. package/dist/__tests__/cron/cron.test.js.map +1 -0
  13. package/dist/__tests__/security/encrypted-fields.test.d.ts +2 -0
  14. package/dist/__tests__/security/encrypted-fields.test.d.ts.map +1 -0
  15. package/dist/__tests__/security/encrypted-fields.test.js +60 -0
  16. package/dist/__tests__/security/encrypted-fields.test.js.map +1 -0
  17. package/dist/__tests__/security/safe-fetch.test.d.ts +2 -0
  18. package/dist/__tests__/security/safe-fetch.test.d.ts.map +1 -0
  19. package/dist/__tests__/security/safe-fetch.test.js +97 -0
  20. package/dist/__tests__/security/safe-fetch.test.js.map +1 -0
  21. package/dist/__tests__/security/ssrf.test.d.ts +2 -0
  22. package/dist/__tests__/security/ssrf.test.d.ts.map +1 -0
  23. package/dist/__tests__/security/ssrf.test.js +209 -0
  24. package/dist/__tests__/security/ssrf.test.js.map +1 -0
  25. package/dist/api/handler-factory.d.ts.map +1 -1
  26. package/dist/api/handler-factory.js +3 -0
  27. package/dist/api/handler-factory.js.map +1 -1
  28. package/dist/api/handlers.d.ts.map +1 -1
  29. package/dist/api/handlers.js +84 -1
  30. package/dist/api/handlers.js.map +1 -1
  31. package/dist/auth/oauth.d.ts +8 -0
  32. package/dist/auth/oauth.d.ts.map +1 -1
  33. package/dist/auth/oauth.js +39 -1
  34. package/dist/auth/oauth.js.map +1 -1
  35. package/dist/auth/password.d.ts +35 -2
  36. package/dist/auth/password.d.ts.map +1 -1
  37. package/dist/auth/password.js +97 -7
  38. package/dist/auth/password.js.map +1 -1
  39. package/dist/auth/session.d.ts +9 -0
  40. package/dist/auth/session.d.ts.map +1 -1
  41. package/dist/auth/session.js +54 -1
  42. package/dist/auth/session.js.map +1 -1
  43. package/dist/cron/index.d.ts +72 -0
  44. package/dist/cron/index.d.ts.map +1 -0
  45. package/dist/cron/index.js +222 -0
  46. package/dist/cron/index.js.map +1 -0
  47. package/dist/security/encrypted-fields.d.ts +9 -0
  48. package/dist/security/encrypted-fields.d.ts.map +1 -1
  49. package/dist/security/encrypted-fields.js +52 -1
  50. package/dist/security/encrypted-fields.js.map +1 -1
  51. package/dist/security/ip-canon.d.ts +71 -0
  52. package/dist/security/ip-canon.d.ts.map +1 -0
  53. package/dist/security/ip-canon.js +352 -0
  54. package/dist/security/ip-canon.js.map +1 -0
  55. package/dist/security/safe-fetch.d.ts +30 -8
  56. package/dist/security/safe-fetch.d.ts.map +1 -1
  57. package/dist/security/safe-fetch.js +32 -6
  58. package/dist/security/safe-fetch.js.map +1 -1
  59. package/dist/security/webhook.d.ts +20 -2
  60. package/dist/security/webhook.d.ts.map +1 -1
  61. package/dist/security/webhook.js +100 -30
  62. package/dist/security/webhook.js.map +1 -1
  63. package/package.json +1 -1
@@ -0,0 +1,60 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { encryptField, decryptField, InvalidEncryptionKeyError, } from '../../security/encrypted-fields.js';
3
+ const VALID_KEY = 'a'.repeat(64); // 64 hex chars = 32 bytes
4
+ const ALT_KEY = 'b'.repeat(64);
5
+ describe('encrypted-fields key validation', () => {
6
+ it('rejects empty key', async () => {
7
+ await expect(encryptField('hello', '')).rejects.toBeInstanceOf(InvalidEncryptionKeyError);
8
+ });
9
+ it('rejects undefined / non-string key', async () => {
10
+ await expect(encryptField('hello', undefined)).rejects.toBeInstanceOf(InvalidEncryptionKeyError);
11
+ });
12
+ it('rejects too-short key', async () => {
13
+ await expect(encryptField('hello', 'a'.repeat(32))).rejects.toBeInstanceOf(InvalidEncryptionKeyError);
14
+ });
15
+ it('rejects too-long key', async () => {
16
+ await expect(encryptField('hello', 'a'.repeat(128))).rejects.toBeInstanceOf(InvalidEncryptionKeyError);
17
+ });
18
+ it('rejects non-hex characters', async () => {
19
+ await expect(encryptField('hello', 'z'.repeat(63) + 'a')).rejects.toBeInstanceOf(InvalidEncryptionKeyError);
20
+ });
21
+ it('rejects the placeholder dev key from the .env example', async () => {
22
+ // "aes256-local-dev-key-change-in-prod" is 35 chars, not 64, and contains '-'.
23
+ await expect(encryptField('hello', 'aes256-local-dev-key-change-in-prod')).rejects.toBeInstanceOf(InvalidEncryptionKeyError);
24
+ });
25
+ it('error message points operators at the random-bytes generator', async () => {
26
+ try {
27
+ await encryptField('hello', 'short');
28
+ throw new Error('should have thrown');
29
+ }
30
+ catch (err) {
31
+ expect(err).toBeInstanceOf(InvalidEncryptionKeyError);
32
+ expect(err.message).toContain('randomBytes(32)');
33
+ }
34
+ });
35
+ });
36
+ describe('encrypted-fields round-trip', () => {
37
+ it('encrypts and decrypts with the same key', async () => {
38
+ const plaintext = 'hello, world — with unicode ✓';
39
+ const encrypted = await encryptField(plaintext, VALID_KEY);
40
+ expect(encrypted).not.toBe(plaintext);
41
+ const decrypted = await decryptField(encrypted, VALID_KEY);
42
+ expect(decrypted).toBe(plaintext);
43
+ });
44
+ it('produces different ciphertext on every call (unique IV)', async () => {
45
+ const a = await encryptField('same input', VALID_KEY);
46
+ const b = await encryptField('same input', VALID_KEY);
47
+ expect(a).not.toBe(b);
48
+ });
49
+ it('rejects ciphertext encrypted with a different key', async () => {
50
+ const encrypted = await encryptField('secret', VALID_KEY);
51
+ await expect(decryptField(encrypted, ALT_KEY)).rejects.toThrow();
52
+ });
53
+ it('rejects tampered ciphertext (AES-GCM auth tag enforces integrity)', async () => {
54
+ const encrypted = await encryptField('secret', VALID_KEY);
55
+ // Flip one byte at the end (inside the auth tag region)
56
+ const tampered = encrypted.slice(0, -2) + (encrypted.slice(-2) === 'ff' ? '00' : 'ff');
57
+ await expect(decryptField(tampered, VALID_KEY)).rejects.toThrow();
58
+ });
59
+ });
60
+ //# sourceMappingURL=encrypted-fields.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encrypted-fields.test.js","sourceRoot":"","sources":["../../../src/__tests__/security/encrypted-fields.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=safe-fetch.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safe-fetch.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/security/safe-fetch.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,97 @@
1
+ import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';
2
+ import { safeFetch, SsrfBlockedError } from '../../security/safe-fetch.js';
3
+ describe('safeFetch — DNS rebinding defense', () => {
4
+ let originalFetch;
5
+ let mockFetch;
6
+ beforeEach(() => {
7
+ originalFetch = globalThis.fetch;
8
+ mockFetch = vi.fn();
9
+ globalThis.fetch = mockFetch;
10
+ });
11
+ afterEach(() => {
12
+ globalThis.fetch = originalFetch;
13
+ });
14
+ it('proceeds with public IP when DNS resolves to a public address', async () => {
15
+ mockFetch.mockResolvedValueOnce(new Response('ok', { status: 200 }));
16
+ const resolver = vi.fn().mockResolvedValue({ safe: true, resolvedIp: '8.8.8.8' });
17
+ const res = await safeFetch('https://example.com/x', { _resolver: resolver });
18
+ expect(res.status).toBe(200);
19
+ expect(resolver).toHaveBeenCalledWith('example.com');
20
+ expect(mockFetch).toHaveBeenCalledTimes(1);
21
+ });
22
+ it('rejects when DNS resolves to a private IP (DNS rebinding defeated)', async () => {
23
+ const resolver = vi.fn().mockResolvedValue({
24
+ safe: false,
25
+ resolvedIp: '127.0.0.1',
26
+ error: 'Resolved IP 127.0.0.1 is in a private range',
27
+ });
28
+ await expect(safeFetch('https://attacker-controlled.tld/probe', { _resolver: resolver })).rejects.toBeInstanceOf(SsrfBlockedError);
29
+ expect(mockFetch).not.toHaveBeenCalled();
30
+ });
31
+ it('rejects when DNS resolves to AWS metadata IP', async () => {
32
+ const resolver = vi.fn().mockResolvedValue({
33
+ safe: false,
34
+ resolvedIp: '169.254.169.254',
35
+ error: 'Resolved IP 169.254.169.254 is in a private range',
36
+ });
37
+ await expect(safeFetch('https://looks-public.com/', { _resolver: resolver })).rejects.toBeInstanceOf(SsrfBlockedError);
38
+ });
39
+ it('best-effort mode: proceeds when resolver throws (default)', async () => {
40
+ mockFetch.mockResolvedValueOnce(new Response('ok'));
41
+ const resolver = vi.fn().mockRejectedValue(new Error('DNS module unavailable'));
42
+ const res = await safeFetch('https://example.com/', { _resolver: resolver });
43
+ expect(res.status).toBe(200);
44
+ });
45
+ it('strict mode: rejects when resolver throws and requireDnsCheck=true', async () => {
46
+ const resolver = vi.fn().mockRejectedValue(new Error('DNS module unavailable'));
47
+ await expect(safeFetch('https://example.com/', {
48
+ _resolver: resolver,
49
+ requireDnsCheck: true,
50
+ })).rejects.toBeInstanceOf(SsrfBlockedError);
51
+ expect(mockFetch).not.toHaveBeenCalled();
52
+ });
53
+ it('rejects URL-string bypass forms before any DNS lookup', async () => {
54
+ const resolver = vi.fn();
55
+ await expect(safeFetch('http://2130706433/', { _resolver: resolver })).rejects.toBeInstanceOf(SsrfBlockedError);
56
+ await expect(safeFetch('http://[::ffff:127.0.0.1]/', { _resolver: resolver })).rejects.toBeInstanceOf(SsrfBlockedError);
57
+ expect(resolver).not.toHaveBeenCalled();
58
+ });
59
+ it('re-validates AND re-resolves every redirect hop', async () => {
60
+ // Hop 1: example.com (public) -> 302 to evil.com
61
+ mockFetch.mockResolvedValueOnce(new Response(null, { status: 302, headers: { Location: 'https://evil.com/inner' } }));
62
+ // Hop 2 should never be issued because resolver flags evil.com as private.
63
+ const resolver = vi
64
+ .fn()
65
+ .mockImplementationOnce(async () => ({ safe: true, resolvedIp: '8.8.8.8' }))
66
+ .mockImplementationOnce(async () => ({
67
+ safe: false,
68
+ resolvedIp: '10.0.0.5',
69
+ error: 'private',
70
+ }));
71
+ await expect(safeFetch('https://example.com/start', {
72
+ _resolver: resolver,
73
+ followRedirects: true,
74
+ })).rejects.toBeInstanceOf(SsrfBlockedError);
75
+ expect(resolver).toHaveBeenCalledTimes(2);
76
+ expect(mockFetch).toHaveBeenCalledTimes(1); // only the first hop went out
77
+ });
78
+ it('default (no follow) returns the redirect response unmodified', async () => {
79
+ mockFetch.mockResolvedValueOnce(new Response(null, { status: 302, headers: { Location: 'https://elsewhere.com/' } }));
80
+ const resolver = vi.fn().mockResolvedValue({ safe: true, resolvedIp: '8.8.8.8' });
81
+ const res = await safeFetch('https://example.com/', { _resolver: resolver });
82
+ expect(res.status).toBe(302);
83
+ expect(mockFetch).toHaveBeenCalledTimes(1);
84
+ // No re-resolve because we didn't follow the redirect.
85
+ expect(resolver).toHaveBeenCalledTimes(1);
86
+ });
87
+ it('honors maxRedirects bound', async () => {
88
+ mockFetch.mockResolvedValue(new Response(null, { status: 302, headers: { Location: 'https://example.com/loop' } }));
89
+ const resolver = vi.fn().mockResolvedValue({ safe: true, resolvedIp: '8.8.8.8' });
90
+ await expect(safeFetch('https://example.com/start', {
91
+ _resolver: resolver,
92
+ followRedirects: true,
93
+ maxRedirects: 2,
94
+ })).rejects.toBeInstanceOf(SsrfBlockedError);
95
+ });
96
+ });
97
+ //# sourceMappingURL=safe-fetch.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safe-fetch.test.js","sourceRoot":"","sources":["../../../src/__tests__/security/safe-fetch.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ssrf.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssrf.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/security/ssrf.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,209 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { canonicalizeHostname, isPrivateAddress } from '../../security/ip-canon.js';
3
+ import { resolveAndCheck, validateWebhookUrl } from '../../security/webhook.js';
4
+ describe('canonicalizeHostname — IPv4 encodings', () => {
5
+ it.each([
6
+ ['127.0.0.1', '127.0.0.1'],
7
+ ['10.0.0.1', '10.0.0.1'],
8
+ ['192.168.1.1', '192.168.1.1'],
9
+ // Decimal: 127*2^24 = 2130706432 → 127.0.0.0; +1 = 127.0.0.1
10
+ ['2130706433', '127.0.0.1'],
11
+ // Octal (0177 = 127, 0 = 0, 0 = 0, 01 = 1)
12
+ ['0177.0.0.1', '127.0.0.1'],
13
+ ['0177.0.0.01', '127.0.0.1'],
14
+ // Hex
15
+ ['0x7f.0.0.1', '127.0.0.1'],
16
+ ['0x7f000001', '127.0.0.1'],
17
+ // Short forms
18
+ ['127.1', '127.0.0.1'],
19
+ ['10.1', '10.0.0.1'],
20
+ ['127.0.1', '127.0.0.1'],
21
+ ])('canonicalizes %s → %s', (input, expected) => {
22
+ const c = canonicalizeHostname(input);
23
+ expect(c.isHostname).toBe(false);
24
+ expect(c.ipv4).toBe(expected);
25
+ });
26
+ it('rejects malformed IPv4 (out-of-range octet)', () => {
27
+ const c = canonicalizeHostname('256.0.0.1');
28
+ expect(c.isHostname).toBe(true); // falls through to "treat as hostname"
29
+ });
30
+ it('rejects malformed octal (digit 8 is invalid)', () => {
31
+ const c = canonicalizeHostname('0888.0.0.1');
32
+ expect(c.isHostname).toBe(true);
33
+ });
34
+ });
35
+ describe('canonicalizeHostname — IPv6 encodings', () => {
36
+ it.each([
37
+ ['::1', '::ffff:127.0.0.1 OR plain'], // checked separately below
38
+ ])('detects IPv6 forms', () => {
39
+ expect(canonicalizeHostname('::1').isHostname).toBe(false);
40
+ });
41
+ it('canonicalizes ::ffff:127.0.0.1 to underlying IPv4', () => {
42
+ const c = canonicalizeHostname('::ffff:127.0.0.1');
43
+ expect(c.ipv4).toBe('127.0.0.1');
44
+ });
45
+ it('canonicalizes ::ffff:7f00:1 (hex form) to underlying IPv4', () => {
46
+ const c = canonicalizeHostname('::ffff:7f00:1');
47
+ expect(c.ipv4).toBe('127.0.0.1');
48
+ });
49
+ it('canonicalizes ::127.0.0.1 (deprecated IPv4-compatible) to underlying IPv4', () => {
50
+ const c = canonicalizeHostname('::127.0.0.1');
51
+ expect(c.ipv4).toBe('127.0.0.1');
52
+ });
53
+ it('handles bracketed IPv6 (URL form)', () => {
54
+ const c = canonicalizeHostname('[::1]');
55
+ expect(c.ipv6).toBeDefined();
56
+ });
57
+ it('rejects IPv6 with multiple :: compressions', () => {
58
+ const c = canonicalizeHostname('::1::1');
59
+ expect(c.isHostname).toBe(false);
60
+ expect(c.isValidIp).toBe(false); // <- Bugbot #3: explicit malformed flag
61
+ expect(c.ipv6).toBeUndefined();
62
+ expect(c.ipv4).toBeUndefined();
63
+ });
64
+ });
65
+ describe('canonicalizeHostname — malformed IP literals', () => {
66
+ // Bugbot review #1/#2/#3 (PR #40): every input that looks like an IP
67
+ // literal but doesn't parse must be flagged with `isValidIp: false` so
68
+ // SSRF gates can fail closed instead of misreading "no IP returned" as
69
+ // "no private IP found".
70
+ it.each([
71
+ '::1::1', // multiple :: compressions
72
+ '::garbage', // non-hex hextet
73
+ 'fe80:::1', // triple colon
74
+ '::1:gg::', // bad chars + multiple ::
75
+ '1:2:3:4:5:6:7:8:9', // too many groups
76
+ 'abcd::1234::5678', // multiple ::
77
+ ])('marks %s as a non-hostname AND non-valid IP', (input) => {
78
+ const c = canonicalizeHostname(input);
79
+ expect(c.isHostname).toBe(false);
80
+ expect(c.isValidIp).toBe(false);
81
+ expect(c.ipv4).toBeUndefined();
82
+ expect(c.ipv6).toBeUndefined();
83
+ });
84
+ it('isPrivateAddress fails closed on the malformed shape', () => {
85
+ // Defense in depth: even if a caller forgets to check `isValidIp`,
86
+ // calling `isPrivateAddress` on a malformed shape returns a non-null
87
+ // reason instead of `null` (which would mean "safe to fetch").
88
+ const c = canonicalizeHostname('::1::1');
89
+ const result = isPrivateAddress(c);
90
+ expect(result).not.toBeNull();
91
+ expect(result?.reason).toMatch(/malformed/i);
92
+ });
93
+ });
94
+ describe('canonicalizeHostname — DNS hostnames pass through', () => {
95
+ it.each(['example.com', 'sub.example.com', 'localhost-but-not-quite.com', 'a-b-c.example'])('leaves %s as a hostname', (input) => {
96
+ const c = canonicalizeHostname(input);
97
+ expect(c.isHostname).toBe(true);
98
+ expect(c.ipv4).toBeUndefined();
99
+ expect(c.ipv6).toBeUndefined();
100
+ });
101
+ });
102
+ describe('isPrivateAddress', () => {
103
+ it.each([
104
+ '127.0.0.1',
105
+ '10.0.0.1',
106
+ '172.16.0.1',
107
+ '172.31.255.254',
108
+ '192.168.1.1',
109
+ '169.254.169.254', // AWS metadata
110
+ '100.64.0.1', // CGNAT
111
+ '0.0.0.0',
112
+ '198.18.0.1', // benchmark range
113
+ '224.0.0.1', // multicast
114
+ '255.255.255.255', // broadcast
115
+ ])('flags %s as private', (ip) => {
116
+ const c = canonicalizeHostname(ip);
117
+ expect(isPrivateAddress(c)).not.toBeNull();
118
+ });
119
+ it.each(['8.8.8.8', '1.1.1.1', '93.184.216.34', '203.0.0.1'])('allows public IP %s', (ip) => {
120
+ const c = canonicalizeHostname(ip);
121
+ expect(isPrivateAddress(c)).toBeNull();
122
+ });
123
+ it.each([
124
+ ['::1', '::1 (IPv6 loopback)'],
125
+ ['::', ':: (unspecified)'],
126
+ ['fe80::1', 'fe80::/10 (IPv6 link-local)'],
127
+ ['fc00::1', 'fc00::/7 (IPv6 unique local)'],
128
+ ['fd12::1', 'fc00::/7 (IPv6 unique local)'],
129
+ ['ff02::1', 'ff00::/8 (IPv6 multicast)'],
130
+ ])('flags IPv6 %s as private', (ip) => {
131
+ const c = canonicalizeHostname(ip);
132
+ expect(isPrivateAddress(c)).not.toBeNull();
133
+ });
134
+ });
135
+ describe('validateWebhookUrl — bypass forms', () => {
136
+ // These are the bypass forms the prior regex-based implementation missed.
137
+ // Each one would have happily POSTed to localhost / metadata IP / RFC1918.
138
+ it.each([
139
+ ['decimal IP', 'http://2130706433/'],
140
+ ['octal IP', 'http://0177.0.0.1/'],
141
+ ['hex IP', 'http://0x7f.0.0.1/'],
142
+ ['mixed hex flat', 'http://0x7f000001/'],
143
+ ['short form', 'http://127.1/'],
144
+ ['IPv4-mapped IPv6', 'http://[::ffff:127.0.0.1]/'],
145
+ ['IPv4-compatible IPv6 (deprecated)', 'http://[::127.0.0.1]/'],
146
+ ['bracketed IPv6 loopback', 'http://[::1]/'],
147
+ ['IPv6 link-local', 'http://[fe80::1]/'],
148
+ ['IPv6 unique-local fd00', 'http://[fd00::1]/'],
149
+ ['CGNAT range', 'http://100.64.0.1/'],
150
+ ['AWS metadata', 'http://169.254.169.254/latest/meta-data/'],
151
+ ['benchmark range', 'http://198.18.0.1/'],
152
+ ['localhost hostname', 'http://localhost/'],
153
+ ['metadata.google.internal', 'http://metadata.google.internal/'],
154
+ ['arbitrary .internal hostname', 'http://my-service.internal/'],
155
+ ])('rejects %s (%s)', (_label, url) => {
156
+ const result = validateWebhookUrl(url);
157
+ expect(result.valid).toBe(false);
158
+ expect(result.error).toBeDefined();
159
+ });
160
+ it.each([
161
+ ['regular https', 'https://example.com/webhook'],
162
+ ['public IP literal', 'http://8.8.8.8/'],
163
+ ['hostname with port', 'https://api.example.com:8443/hooks/123'],
164
+ ])('allows %s (%s)', (_label, url) => {
165
+ const result = validateWebhookUrl(url);
166
+ expect(result.valid).toBe(true);
167
+ });
168
+ it('rejects non-http(s) protocols', () => {
169
+ expect(validateWebhookUrl('file:///etc/passwd').valid).toBe(false);
170
+ expect(validateWebhookUrl('gopher://example.com/').valid).toBe(false);
171
+ expect(validateWebhookUrl('ftp://example.com/').valid).toBe(false);
172
+ expect(validateWebhookUrl('javascript:alert(1)').valid).toBe(false);
173
+ });
174
+ it('rejects malformed URLs', () => {
175
+ expect(validateWebhookUrl('not a url').valid).toBe(false);
176
+ expect(validateWebhookUrl('').valid).toBe(false);
177
+ });
178
+ // Bugbot review #1 (PR #40): malformed IP-shaped hostnames previously
179
+ // slipped through because canonicalizeHostname returned `{isHostname:
180
+ // false}` with no IP fields, and isPrivateAddress returned null —
181
+ // which the validator interpreted as "safe". They must fail closed.
182
+ it.each([
183
+ 'http://[::1::1]/', // multi :: IPv6
184
+ 'http://[fe80:::1]/', // triple colon
185
+ 'http://[abcd::1234::5678]/', // multi ::
186
+ ])('rejects malformed IP literal in URL: %s', (url) => {
187
+ const result = validateWebhookUrl(url);
188
+ expect(result.valid).toBe(false);
189
+ expect(result.error).toMatch(/malformed|invalid/i);
190
+ });
191
+ });
192
+ describe('resolveAndCheck — IP literal short-circuit', () => {
193
+ it('rejects malformed IPv6 literal without DNS lookup', async () => {
194
+ // Bugbot review #2 (PR #40): the IP-literal short-circuit path also
195
+ // had the malformed-IP fail-open bug. Verify it now fails closed.
196
+ const result = await resolveAndCheck('::1::1');
197
+ expect(result.safe).toBe(false);
198
+ expect(result.error).toMatch(/malformed/i);
199
+ });
200
+ it('still allows public IP literals', async () => {
201
+ const result = await resolveAndCheck('8.8.8.8');
202
+ expect(result.safe).toBe(true);
203
+ });
204
+ it('still rejects private IP literals', async () => {
205
+ const result = await resolveAndCheck('127.0.0.1');
206
+ expect(result.safe).toBe(false);
207
+ });
208
+ });
209
+ //# sourceMappingURL=ssrf.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssrf.test.js","sourceRoot":"","sources":["../../../src/__tests__/security/ssrf.test.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"handler-factory.d.ts","sourceRoot":"","sources":["../../src/api/handler-factory.ts"],"names":[],"mappings":"AAsDA,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;CAC5C;AAMD,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,uBAA4B,IAkFtC,SAAS,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CAgHnE"}
1
+ {"version":3,"file":"handler-factory.d.ts","sourceRoot":"","sources":["../../src/api/handler-factory.ts"],"names":[],"mappings":"AAyDA,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;CAC5C;AAMD,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,uBAA4B,IAkFtC,SAAS,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CAgHnE"}
@@ -22,6 +22,8 @@ import { setStorageAdapter } from '../storage/index.js';
22
22
  * /auth/oauth/... — OAuth init + callback redirects
23
23
  * /setup/create-admin — only reachable on a fresh install
24
24
  * /forms/:id/submit — public form submissions (regex below)
25
+ * /cron/... — Vercel Cron / external schedulers; auth is via
26
+ * Authorization: Bearer ${CRON_SECRET} instead
25
27
  */
26
28
  const CSRF_EXEMPT_PATHS = [
27
29
  '/auth/login',
@@ -31,6 +33,7 @@ const CSRF_EXEMPT_PATHS = [
31
33
  '/auth/reset-password',
32
34
  '/auth/oauth/',
33
35
  '/setup/create-admin',
36
+ '/cron/',
34
37
  ];
35
38
  function isCsrfExemptPath(pathname) {
36
39
  if (CSRF_EXEMPT_PATHS.some((p) => pathname === p ||
@@ -1 +1 @@
1
- {"version":3,"file":"handler-factory.js","sourceRoot":"","sources":["../../src/api/handler-factory.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"handler-factory.js","sourceRoot":"","sources":["../../src/api/handler-factory.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/api/handlers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAwW5C,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAS9E;AAkKD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAi1IzD"}
1
+ {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/api/handlers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAyW5C,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAS9E;AAkKD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAu6IzD"}
@@ -1,5 +1,5 @@
1
1
  import { listDocuments, getDocument, createDocument, updateDocument, deleteDocument, getGlobal, updateGlobal, } from '../actions.js';
2
- import { verifyPassword } from '../auth/password.js';
2
+ import { verifyPassword, hashPassword, needsRehash, compareToDummyHash } from '../auth/password.js';
3
3
  import { createSession, verifySession, revokeSession } from '../auth/session.js';
4
4
  import { createPasswordReset, executePasswordReset } from '../auth/reset.js';
5
5
  import { checkSetupRequired, createInitialAdmin } from '../setup/index.js';
@@ -12,6 +12,7 @@ import { logEvent } from '../security/audit.js';
12
12
  import { applyFieldAccess } from '../security/access.js';
13
13
  import { createPreviewAdapter } from '../preview/index.js';
14
14
  import { schedulingCronHandler } from '../scheduling/index.js';
15
+ import { isAuthorizedCronRequest, processCleanup, processSeoScan } from '../cron/index.js';
15
16
  import { verifyCaptcha, getCaptchaConfig } from '../security/captcha.js';
16
17
  import { checkForUpdates } from '../upgrade/version-check.js';
17
18
  import { createUpgradePR } from '../upgrade/upgrade-pr.js';
@@ -563,10 +564,18 @@ export function registerCMSRoutes(router) {
563
564
  const user = await d.user.findFirst({
564
565
  where: { email: email.toLowerCase().trim() },
565
566
  });
567
+ // User-enumeration timing defense (H5): when the email doesn't exist,
568
+ // OR when the account is OAuth-only (no passwordHash), still spend
569
+ // the same ~100-200ms running PBKDF2 against a dummy hash before
570
+ // returning the error. Without this, attackers can distinguish
571
+ // "no such user" / "OAuth-only" / "wrong password" by the response
572
+ // delta, enabling targeted phishing & credential stuffing.
566
573
  if (!user) {
574
+ await compareToDummyHash(password);
567
575
  return errorResponse('Invalid email or password', 401);
568
576
  }
569
577
  if (!user.passwordHash) {
578
+ await compareToDummyHash(password);
570
579
  return errorResponse('This account uses social login. Please sign in with your OAuth provider.', 400);
571
580
  }
572
581
  const passwordValid = await verifyPassword(password, user.passwordHash);
@@ -582,6 +591,20 @@ export function registerCMSRoutes(router) {
582
591
  if (!user.isActive) {
583
592
  return errorResponse('Account is deactivated', 403);
584
593
  }
594
+ // H6: opportunistically upgrade stored hashes that use weaker
595
+ // PBKDF2 parameters than the current policy. We have the plaintext
596
+ // here (and we know it's correct) — re-hash and persist. Wrapped in
597
+ // try/catch so a transient DB failure never fails an otherwise-valid
598
+ // login; the user gets in, the upgrade just runs again next time.
599
+ if (needsRehash(user.passwordHash)) {
600
+ try {
601
+ const upgraded = await hashPassword(password);
602
+ await d.user.update({ where: { id: user.id }, data: { passwordHash: upgraded } });
603
+ }
604
+ catch (err) {
605
+ console.warn('[actuate][login] password rehash skipped:', err instanceof Error ? err.message : err);
606
+ }
607
+ }
585
608
  if (user.totpEnabled) {
586
609
  // Hand back an opaque short-lived token instead of the raw userId.
587
610
  // The /auth/totp/login endpoint will verify both this token and a
@@ -3472,6 +3495,66 @@ export function registerCMSRoutes(router) {
3472
3495
  return internalError(err, 'scheduling run');
3473
3496
  }
3474
3497
  });
3498
+ // ---------------------------------------------------------------------------
3499
+ // Cron endpoints — auth via `Authorization: Bearer ${CRON_SECRET}`.
3500
+ //
3501
+ // **HTTP method:** registered as GET because Vercel Cron sends GET requests
3502
+ // (https://vercel.com/docs/cron-jobs). We also register POST aliases so
3503
+ // self-hosted schedulers that POST (k8s CronJob, GH Actions step running
3504
+ // `curl -X POST`, EventBridge HTTP target) keep working without changes.
3505
+ //
3506
+ // Vercel Cron sets the `Authorization: Bearer <CRON_SECRET>` header
3507
+ // automatically when `CRON_SECRET` is defined in the project environment.
3508
+ // When CRON_SECRET is unset, requests are rejected — fail-closed so a
3509
+ // misconfigured deploy never exposes these to the public.
3510
+ //
3511
+ // CSRF is NOT required: these endpoints have no session and the body carries
3512
+ // no admin intent — auth is exclusively via the Bearer token. They are also
3513
+ // listed in `CSRF_EXEMPT_PATHS` in handler-factory.ts so the global CSRF
3514
+ // gate doesn't block them.
3515
+ // ---------------------------------------------------------------------------
3516
+ const cronPublish = async (request) => {
3517
+ try {
3518
+ if (!isAuthorizedCronRequest(request.headers.get('authorization'))) {
3519
+ return errorResponse('Unauthorized', 401);
3520
+ }
3521
+ const result = await schedulingCronHandler(db());
3522
+ return json({ data: result });
3523
+ }
3524
+ catch (err) {
3525
+ return internalError(err, 'cron publish');
3526
+ }
3527
+ };
3528
+ router.get('/cron/publish', cronPublish);
3529
+ router.post('/cron/publish', cronPublish);
3530
+ const cronCleanup = async (request) => {
3531
+ try {
3532
+ if (!isAuthorizedCronRequest(request.headers.get('authorization'))) {
3533
+ return errorResponse('Unauthorized', 401);
3534
+ }
3535
+ const result = await processCleanup(db());
3536
+ return json({ data: result });
3537
+ }
3538
+ catch (err) {
3539
+ return internalError(err, 'cron cleanup');
3540
+ }
3541
+ };
3542
+ router.get('/cron/cleanup', cronCleanup);
3543
+ router.post('/cron/cleanup', cronCleanup);
3544
+ const cronSeoScan = async (request) => {
3545
+ try {
3546
+ if (!isAuthorizedCronRequest(request.headers.get('authorization'))) {
3547
+ return errorResponse('Unauthorized', 401);
3548
+ }
3549
+ const result = await processSeoScan(db());
3550
+ return json({ data: result });
3551
+ }
3552
+ catch (err) {
3553
+ return internalError(err, 'cron seo-scan');
3554
+ }
3555
+ };
3556
+ router.get('/cron/seo-scan', cronSeoScan);
3557
+ router.post('/cron/seo-scan', cronSeoScan);
3475
3558
  router.get('/scheduling/calendar', async (request) => {
3476
3559
  try {
3477
3560
  const auth = await requireAuth(request);