@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
@@ -7,6 +7,7 @@ import { createRateLimiter } from '../security/rate-limit.js';
7
7
  import { getClientIp } from '../security/client-ip.js';
8
8
  import { autoSeedAdmin } from '../setup/index.js';
9
9
  import { setStorageAdapter } from '../storage/index.js';
10
+ import { getActuateConfig, setActuateConfig, setActuateCoreVersion } from '../config/runtime.js';
10
11
  /**
11
12
  * Routes that may legitimately receive a state-mutating request without a
12
13
  * CSRF token. Matched by exact prefix on the rewritten (CMS-relative) path,
@@ -22,6 +23,8 @@ import { setStorageAdapter } from '../storage/index.js';
22
23
  * /auth/oauth/... — OAuth init + callback redirects
23
24
  * /setup/create-admin — only reachable on a fresh install
24
25
  * /forms/:id/submit — public form submissions (regex below)
26
+ * /cron/... — Vercel Cron / external schedulers; auth is via
27
+ * Authorization: Bearer ${CRON_SECRET} instead
25
28
  */
26
29
  const CSRF_EXEMPT_PATHS = [
27
30
  '/auth/login',
@@ -31,6 +34,7 @@ const CSRF_EXEMPT_PATHS = [
31
34
  '/auth/reset-password',
32
35
  '/auth/oauth/',
33
36
  '/setup/create-admin',
37
+ '/cron/',
34
38
  ];
35
39
  function isCsrfExemptPath(pathname) {
36
40
  if (CSRF_EXEMPT_PATHS.some((p) => pathname === p ||
@@ -45,11 +49,13 @@ const _require = createRequire(import.meta.url);
45
49
  const { version: CMS_CORE_VERSION } = _require('../../package.json');
46
50
  let cachedGraphQL = null;
47
51
  export function handleActuateAPI(options = {}) {
48
- ;
49
- globalThis.__actuateCoreVersion = CMS_CORE_VERSION;
52
+ setActuateCoreVersion(CMS_CORE_VERSION);
50
53
  if (options.config) {
51
- ;
52
- globalThis.__actuateConfig = options.config;
54
+ // Cast through `unknown` only to satisfy the typed setter; the public
55
+ // signature accepts `unknown` deliberately because consumers may have
56
+ // their own typings. Plugins and downstream code use `getActuateConfig()`
57
+ // which gives them the ActuateCMSConfig shape.
58
+ setActuateConfig(options.config);
53
59
  const config = options.config;
54
60
  const hooks = [];
55
61
  if (Array.isArray(config.plugins)) {
@@ -175,7 +181,7 @@ export function handleActuateAPI(options = {}) {
175
181
  });
176
182
  }
177
183
  }
178
- const config = globalThis.__actuateConfig;
184
+ const config = getActuateConfig();
179
185
  if (config) {
180
186
  if (!cachedGraphQL) {
181
187
  const { createGraphQLHandler } = await import('../graphql/index.js');
@@ -193,7 +199,10 @@ export function handleActuateAPI(options = {}) {
193
199
  method: request.method,
194
200
  headers: request.headers,
195
201
  body: request.body,
196
- // @ts-ignore -- duplex is needed for streaming bodies
202
+ // @ts-expect-error -- `duplex` is required for streaming bodies but
203
+ // not yet in the lib.dom RequestInit type. Using @ts-expect-error
204
+ // (vs @ts-ignore) ensures this directive itself fails when a future
205
+ // TS lib update adds the property, prompting us to remove the cast.
197
206
  duplex: 'half',
198
207
  });
199
208
  return router.handle(rewrittenRequest);
@@ -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;AAgKD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAy+IzD"}
@@ -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';
@@ -30,6 +31,8 @@ import { enforceSessionLimits } from '../security/session-limits.js';
30
31
  import { verifyReauth } from '../security/reauth.js';
31
32
  import { validateMimeType, checkMagicBytes } from '../security/upload.js';
32
33
  import { sanitizeHtml } from '../security/sanitize.js';
34
+ import { getActuateConfig, getActuateCoreVersion } from '../config/runtime.js';
35
+ import { validateEnvShape } from '../diagnostics/env.js';
33
36
  // Opaque dynamic import so Turbopack/webpack won't statically analyze the specifier.
34
37
  // Returns { put, del, ... } from @vercel/blob when available.
35
38
  async function importBlobStorage() {
@@ -243,9 +246,7 @@ const ALLOWED_SORT_FIELDS = new Set([
243
246
  let _secretMissing = false;
244
247
  let _secretWarningLogged = false;
245
248
  function getSessionSecret() {
246
- const secret = process.env.CMS_SECRET ??
247
- process.env.CMS_SESSION_SECRET ??
248
- globalThis.__actuateConfig?.secret;
249
+ const secret = process.env.CMS_SECRET ?? process.env.CMS_SESSION_SECRET ?? getActuateConfig()?.secret;
249
250
  if (!secret) {
250
251
  _secretMissing = true;
251
252
  if (!_secretWarningLogged) {
@@ -416,9 +417,9 @@ const MAX_CONCURRENT_SESSIONS = 5;
416
417
  async function enforceSessionLimitsForUser(d, userId) {
417
418
  if (!hasModel(d, 'session'))
418
419
  return;
419
- const config = globalThis.__actuateConfig?.auth ?? {};
420
- const max = typeof config.maxConcurrentSessions === 'number' && config.maxConcurrentSessions > 0
421
- ? config.maxConcurrentSessions
420
+ const auth = getActuateConfig()?.auth;
421
+ const max = typeof auth?.maxConcurrentSessions === 'number' && auth.maxConcurrentSessions > 0
422
+ ? auth.maxConcurrentSessions
422
423
  : MAX_CONCURRENT_SESSIONS;
423
424
  const active = await d.session.findMany({
424
425
  where: { userId, revokedAt: null, expiresAt: { gt: new Date() } },
@@ -433,7 +434,7 @@ async function enforceSessionLimitsForUser(d, userId) {
433
434
  }
434
435
  }
435
436
  function getAdminPath() {
436
- return (process.env.ACTUATE_ADMIN_PATH ?? globalThis.__actuateConfig?.admin?.path ?? '/admin');
437
+ return process.env.ACTUATE_ADMIN_PATH ?? getActuateConfig()?.admin?.path ?? '/admin';
437
438
  }
438
439
  class ModelNotAvailableError extends Error {
439
440
  model;
@@ -504,7 +505,7 @@ export function registerCMSRoutes(router) {
504
505
  // OpenAPI spec
505
506
  // ---------------------------------------------------------------------------
506
507
  router.get('/openapi.json', async () => {
507
- const config = globalThis.__actuateConfig;
508
+ const config = getActuateConfig();
508
509
  if (!config)
509
510
  return errorResponse('CMS not configured', 500);
510
511
  const spec = generateOpenAPISpec(config);
@@ -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
@@ -673,7 +696,7 @@ export function registerCMSRoutes(router) {
673
696
  if (!hasModel(d, 'user'))
674
697
  return modelNotAvailable('user');
675
698
  const siteUrl = process.env.NEXT_PUBLIC_SITE_URL ?? new URL(request.url).origin;
676
- const cmsConfig = globalThis.__actuateConfig;
699
+ const cmsConfig = getActuateConfig();
677
700
  await createPasswordReset(d, email.toLowerCase().trim(), {
678
701
  siteUrl,
679
702
  platform: cmsConfig?.platform,
@@ -1054,7 +1077,7 @@ export function registerCMSRoutes(router) {
1054
1077
  };
1055
1078
  const cookies = parseCookieHeader(request.headers.get('cookie') ?? '');
1056
1079
  const expectedNonce = cookies['actuate_oauth_nonce'] ?? null;
1057
- const cmsConfig = globalThis.__actuateConfig;
1080
+ const cmsConfig = getActuateConfig();
1058
1081
  const allowSelfSignup = cmsConfig?.auth?.oauth?.allowSelfSignup === true;
1059
1082
  const result = await handleOAuthCallback(provider, code, stateToken, oauthProviders, secret, db(), { expectedNonce, allowSelfSignup });
1060
1083
  const isProduction = process.env.NODE_ENV === 'production';
@@ -1107,7 +1130,7 @@ export function registerCMSRoutes(router) {
1107
1130
  locale: url.searchParams.get('locale') ?? undefined,
1108
1131
  folderId: url.searchParams.get('folderId') ?? undefined,
1109
1132
  }, ctx);
1110
- const collectionConfig = globalThis.__actuateConfig?.collections?.[params.slug];
1133
+ const collectionConfig = getActuateConfig()?.collections?.[params.slug];
1111
1134
  const fields = collectionConfig?.fields;
1112
1135
  if (fields && result.docs.length > 0) {
1113
1136
  const user = { id: auth.session.userId, role: auth.session.role };
@@ -1307,7 +1330,21 @@ export function registerCMSRoutes(router) {
1307
1330
  const auth = await requireAuth(request);
1308
1331
  if (auth.error)
1309
1332
  return auth.error;
1310
- const contentLength = parseInt(request.headers.get('content-length') ?? '0', 10);
1333
+ // Reject *before* buffering the body. We require a valid content-length
1334
+ // header so that chunked / no-length requests (which would otherwise
1335
+ // bypass this gate and allow `request.formData()` to buffer unbounded
1336
+ // memory) are rejected up front. The multipart envelope is always
1337
+ // larger than the underlying file, so checking against MAX_UPLOAD_BYTES
1338
+ // here is a safe upper bound — a 50 MB file cannot fit inside a 50 MB
1339
+ // request body.
1340
+ const contentLengthHeader = request.headers.get('content-length');
1341
+ if (!contentLengthHeader) {
1342
+ return errorResponse('Content-Length header is required for uploads (chunked encoding is not supported)', 411);
1343
+ }
1344
+ const contentLength = parseInt(contentLengthHeader, 10);
1345
+ if (!Number.isFinite(contentLength) || contentLength <= 0) {
1346
+ return errorResponse('Invalid Content-Length header', 400);
1347
+ }
1311
1348
  if (contentLength > MAX_UPLOAD_BYTES) {
1312
1349
  return errorResponse('File exceeds maximum size of 50MB', 413);
1313
1350
  }
@@ -1320,7 +1357,11 @@ export function registerCMSRoutes(router) {
1320
1357
  const originalFilename = file.name;
1321
1358
  const contentType = file.type;
1322
1359
  const originalSize = file.size;
1323
- if (originalSize > 50 * 1024 * 1024) {
1360
+ // Belt-and-braces: even with the header check above, re-validate the
1361
+ // *actual* file size after parsing in case the multipart envelope
1362
+ // disagreed with the header. This must come BEFORE any further
1363
+ // processing (magic byte read, SVG sanitization, blob upload).
1364
+ if (originalSize > MAX_UPLOAD_BYTES) {
1324
1365
  return errorResponse('File exceeds maximum size of 50MB', 413);
1325
1366
  }
1326
1367
  // 1. Block file types that aren't on our allowlist outright.
@@ -1705,7 +1746,7 @@ export function registerCMSRoutes(router) {
1705
1746
  const session = await extractSession(request);
1706
1747
  if (!session)
1707
1748
  return errorResponse('Unauthorized', 401);
1708
- const coreVersion = globalThis.__actuateCoreVersion ?? '0.1.0';
1749
+ const coreVersion = getActuateCoreVersion() ?? '0.1.0';
1709
1750
  const info = await checkForUpdates(coreVersion);
1710
1751
  const saved = await getUpdateConfig();
1711
1752
  return json({
@@ -1808,7 +1849,7 @@ export function registerCMSRoutes(router) {
1808
1849
  if (!owner || !repo) {
1809
1850
  return errorResponse('GitHub repository not configured. Go to Settings > Updates to add one.', 400);
1810
1851
  }
1811
- const coreVersion = globalThis.__actuateCoreVersion ?? '0.1.0';
1852
+ const coreVersion = getActuateCoreVersion() ?? '0.1.0';
1812
1853
  const result = await createUpgradePR({
1813
1854
  owner,
1814
1855
  repo,
@@ -1893,8 +1934,43 @@ export function registerCMSRoutes(router) {
1893
1934
  'webhookDeliveryLog',
1894
1935
  ];
1895
1936
  router.get('/health', async () => {
1896
- const cmsVersion = globalThis.__actuateCoreVersion ?? '0.0.0';
1937
+ const cmsVersion = getActuateCoreVersion() ?? '0.0.0';
1897
1938
  const models = {};
1939
+ // M6: validate the *shape* of every well-known env var, not just presence.
1940
+ // A 31-char CMS_SECRET or a placeholder CMS_ENCRYPTION_KEY now reports as
1941
+ // a hard error here instead of crashing at the first runtime use.
1942
+ //
1943
+ // Bugbot review (PR #43): CMS_SECRET has *three* legitimate sources —
1944
+ // `process.env.CMS_SECRET`, the legacy `process.env.CMS_SESSION_SECRET`,
1945
+ // and `getActuateConfig()?.secret` (config-file path documented in the
1946
+ // missing-secret warning message itself). `getSessionSecret()` checks all
1947
+ // three; `isSecretMissing()` consequently returns false when any of them
1948
+ // is set. If `validateEnvShape()` only inspected `process.env.CMS_SECRET`,
1949
+ // a config-file deploy would get `secretConfigured: true` AND
1950
+ // `status: "unhealthy"` simultaneously — a contradiction that would trip
1951
+ // monitoring / load-balancer health probes for no real fault. We pass a
1952
+ // wrapped `EnvSource` so the validator sees what the runtime would
1953
+ // actually resolve.
1954
+ const env = validateEnvShape({
1955
+ get(name) {
1956
+ if (name === 'CMS_SECRET') {
1957
+ return (process.env.CMS_SECRET ?? process.env.CMS_SESSION_SECRET ?? getActuateConfig()?.secret);
1958
+ }
1959
+ return process.env[name];
1960
+ },
1961
+ });
1962
+ // Derive overall status from env validation + model availability + DB
1963
+ // connection. `env.errorCount > 0` outranks every model/DB issue because
1964
+ // it represents a *deployment* misconfig the operator must fix before any
1965
+ // request will succeed — pretending the deploy is "degraded" when secrets
1966
+ // are malformed defeats the M6 goal of catching that at /health time.
1967
+ function deriveStatus(dbConnected, allModelsAvailable) {
1968
+ if (env.errorCount > 0)
1969
+ return 'unhealthy';
1970
+ if (allModelsAvailable && dbConnected)
1971
+ return 'healthy';
1972
+ return 'degraded';
1973
+ }
1898
1974
  let d;
1899
1975
  try {
1900
1976
  d = db();
@@ -1904,9 +1980,10 @@ export function registerCMSRoutes(router) {
1904
1980
  models[m] = false;
1905
1981
  return json({
1906
1982
  data: {
1907
- status: 'degraded',
1983
+ status: deriveStatus(false, false),
1908
1984
  version: cmsVersion,
1909
1985
  secretConfigured: !isSecretMissing(),
1986
+ env,
1910
1987
  models,
1911
1988
  databaseConnected: false,
1912
1989
  },
@@ -1932,11 +2009,13 @@ export function registerCMSRoutes(router) {
1932
2009
  }
1933
2010
  }
1934
2011
  const allAvailable = Object.values(models).every(Boolean);
2012
+ const status = deriveStatus(dbConnected, allAvailable);
1935
2013
  return json({
1936
2014
  data: {
1937
- status: allAvailable ? 'healthy' : 'degraded',
2015
+ status,
1938
2016
  version: cmsVersion,
1939
2017
  secretConfigured: !isSecretMissing(),
2018
+ env,
1940
2019
  models,
1941
2020
  databaseConnected: dbConnected,
1942
2021
  },
@@ -2307,9 +2386,9 @@ export function registerCMSRoutes(router) {
2307
2386
  });
2308
2387
  (async () => {
2309
2388
  try {
2310
- const config = globalThis.__actuateConfig;
2311
- const hooks = [...(config?.plugins?.forms?.hooks ?? []), ...(config?._pluginHooks ?? [])];
2312
- const formHooks = hooks.filter((h) => h.event === 'afterCreate:form-submissions');
2389
+ const config = getActuateConfig();
2390
+ const hooks = (config?._pluginHooks ?? []);
2391
+ const formHooks = hooks.filter((h) => h?.event === 'afterCreate:form-submissions');
2313
2392
  for (const hook of formHooks) {
2314
2393
  await hook.handler({ formId, data: body.fields });
2315
2394
  }
@@ -2382,7 +2461,7 @@ export function registerCMSRoutes(router) {
2382
2461
  if (!['http:', 'https:'].includes(destUrl.protocol)) {
2383
2462
  return errorResponse('Invalid destination URL', 400);
2384
2463
  }
2385
- const cmsConfig = globalThis.__actuateConfig;
2464
+ const cmsConfig = getActuateConfig();
2386
2465
  const allowed = new Set([
2387
2466
  ...(Array.isArray(cmsConfig?.redirects?.allowedExternalHosts)
2388
2467
  ? cmsConfig.redirects.allowedExternalHosts.map((h) => h.toLowerCase())
@@ -2944,7 +3023,7 @@ export function registerCMSRoutes(router) {
2944
3023
  // ---------------------------------------------------------------------------
2945
3024
  const MAX_RESOLVE_DEPTH = 10;
2946
3025
  async function resolveLayout(path, docData, matchedCollection) {
2947
- const config = globalThis.__actuateConfig;
3026
+ const config = getActuateConfig();
2948
3027
  const layoutConfig = config?.layout;
2949
3028
  if (!layoutConfig?.regions)
2950
3029
  return {};
@@ -3040,7 +3119,7 @@ export function registerCMSRoutes(router) {
3040
3119
  .replace(/^\/|\/$/g, '')
3041
3120
  .split('/')
3042
3121
  .filter(Boolean);
3043
- const configCollections = globalThis.__actuateConfig?.collections ?? {};
3122
+ const configCollections = getActuateConfig()?.collections ?? {};
3044
3123
  const collectionDefs = Object.values(configCollections);
3045
3124
  let matchedCollection = null;
3046
3125
  let docSlug = null;
@@ -3472,6 +3551,66 @@ export function registerCMSRoutes(router) {
3472
3551
  return internalError(err, 'scheduling run');
3473
3552
  }
3474
3553
  });
3554
+ // ---------------------------------------------------------------------------
3555
+ // Cron endpoints — auth via `Authorization: Bearer ${CRON_SECRET}`.
3556
+ //
3557
+ // **HTTP method:** registered as GET because Vercel Cron sends GET requests
3558
+ // (https://vercel.com/docs/cron-jobs). We also register POST aliases so
3559
+ // self-hosted schedulers that POST (k8s CronJob, GH Actions step running
3560
+ // `curl -X POST`, EventBridge HTTP target) keep working without changes.
3561
+ //
3562
+ // Vercel Cron sets the `Authorization: Bearer <CRON_SECRET>` header
3563
+ // automatically when `CRON_SECRET` is defined in the project environment.
3564
+ // When CRON_SECRET is unset, requests are rejected — fail-closed so a
3565
+ // misconfigured deploy never exposes these to the public.
3566
+ //
3567
+ // CSRF is NOT required: these endpoints have no session and the body carries
3568
+ // no admin intent — auth is exclusively via the Bearer token. They are also
3569
+ // listed in `CSRF_EXEMPT_PATHS` in handler-factory.ts so the global CSRF
3570
+ // gate doesn't block them.
3571
+ // ---------------------------------------------------------------------------
3572
+ const cronPublish = async (request) => {
3573
+ try {
3574
+ if (!isAuthorizedCronRequest(request.headers.get('authorization'))) {
3575
+ return errorResponse('Unauthorized', 401);
3576
+ }
3577
+ const result = await schedulingCronHandler(db());
3578
+ return json({ data: result });
3579
+ }
3580
+ catch (err) {
3581
+ return internalError(err, 'cron publish');
3582
+ }
3583
+ };
3584
+ router.get('/cron/publish', cronPublish);
3585
+ router.post('/cron/publish', cronPublish);
3586
+ const cronCleanup = async (request) => {
3587
+ try {
3588
+ if (!isAuthorizedCronRequest(request.headers.get('authorization'))) {
3589
+ return errorResponse('Unauthorized', 401);
3590
+ }
3591
+ const result = await processCleanup(db());
3592
+ return json({ data: result });
3593
+ }
3594
+ catch (err) {
3595
+ return internalError(err, 'cron cleanup');
3596
+ }
3597
+ };
3598
+ router.get('/cron/cleanup', cronCleanup);
3599
+ router.post('/cron/cleanup', cronCleanup);
3600
+ const cronSeoScan = async (request) => {
3601
+ try {
3602
+ if (!isAuthorizedCronRequest(request.headers.get('authorization'))) {
3603
+ return errorResponse('Unauthorized', 401);
3604
+ }
3605
+ const result = await processSeoScan(db());
3606
+ return json({ data: result });
3607
+ }
3608
+ catch (err) {
3609
+ return internalError(err, 'cron seo-scan');
3610
+ }
3611
+ };
3612
+ router.get('/cron/seo-scan', cronSeoScan);
3613
+ router.post('/cron/seo-scan', cronSeoScan);
3475
3614
  router.get('/scheduling/calendar', async (request) => {
3476
3615
  try {
3477
3616
  const auth = await requireAuth(request);
@@ -3496,7 +3635,7 @@ export function registerCMSRoutes(router) {
3496
3635
  router.get('/public/globals/:slug', async (_request, params) => {
3497
3636
  try {
3498
3637
  const slug = params.slug;
3499
- const globalConfig = globalThis.__actuateConfig?.globals?.[slug];
3638
+ const globalConfig = getActuateConfig()?.globals?.[slug];
3500
3639
  if (!globalConfig) {
3501
3640
  return errorResponse('Global not found', 404);
3502
3641
  }