From 022ae239820074837116c90c322fe8b17be3541a Mon Sep 17 00:00:00 2001 From: Konstantin Wohlwend Date: Sat, 23 Aug 2025 14:07:00 -0700 Subject: [PATCH 1/6] Trusted domains size limit hotfix --- .../src/app/api/latest/integrations/custom/domains/crud.tsx | 3 +++ .../src/app/api/latest/integrations/neon/domains/crud.tsx | 3 +++ 2 files changed, 6 insertions(+) diff --git a/apps/backend/src/app/api/latest/integrations/custom/domains/crud.tsx b/apps/backend/src/app/api/latest/integrations/custom/domains/crud.tsx index 8f97d100ac..d763ec8db0 100644 --- a/apps/backend/src/app/api/latest/integrations/custom/domains/crud.tsx +++ b/apps/backend/src/app/api/latest/integrations/custom/domains/crud.tsx @@ -57,6 +57,9 @@ export const domainCrudHandlers = createLazyProxy(() => createCrudHandlers(domai }), onCreate: async ({ auth, data, params }) => { const oldDomains = auth.tenancy.config.domains.trustedDomains; + if (oldDomains.length > 1000) { + throw new StatusError(400, "This project has more than 1000 trusted domains. This is not supported. Please delete some domains to add a new one, or use wildcard domains instead."); + } await projectsCrudHandlers.adminUpdate({ data: { config: { diff --git a/apps/backend/src/app/api/latest/integrations/neon/domains/crud.tsx b/apps/backend/src/app/api/latest/integrations/neon/domains/crud.tsx index 6bce0b69b4..d61b2fc7b3 100644 --- a/apps/backend/src/app/api/latest/integrations/neon/domains/crud.tsx +++ b/apps/backend/src/app/api/latest/integrations/neon/domains/crud.tsx @@ -56,6 +56,9 @@ export const domainCrudHandlers = createLazyProxy(() => createCrudHandlers(domai }), onCreate: async ({ auth, data, params }) => { const oldDomains = auth.tenancy.config.domains.trustedDomains; + if (oldDomains.length > 1000) { + throw new StatusError(400, "This project has more than 1000 trusted domains. This is not supported. Please delete some domains to add a new one, or use wildcard domains instead."); + } await projectsCrudHandlers.adminUpdate({ data: { config: { From 168b01c2523345fb315538e27d3e7777f8da3626 Mon Sep 17 00:00:00 2001 From: Konstantin Wohlwend Date: Sat, 23 Aug 2025 14:24:25 -0700 Subject: [PATCH 2/6] Fix domain check --- .../src/app/api/latest/integrations/custom/domains/crud.tsx | 2 +- .../src/app/api/latest/integrations/neon/domains/crud.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/backend/src/app/api/latest/integrations/custom/domains/crud.tsx b/apps/backend/src/app/api/latest/integrations/custom/domains/crud.tsx index d763ec8db0..79b62eed53 100644 --- a/apps/backend/src/app/api/latest/integrations/custom/domains/crud.tsx +++ b/apps/backend/src/app/api/latest/integrations/custom/domains/crud.tsx @@ -57,7 +57,7 @@ export const domainCrudHandlers = createLazyProxy(() => createCrudHandlers(domai }), onCreate: async ({ auth, data, params }) => { const oldDomains = auth.tenancy.config.domains.trustedDomains; - if (oldDomains.length > 1000) { + if (Object.keys(oldDomains).length > 1000) { throw new StatusError(400, "This project has more than 1000 trusted domains. This is not supported. Please delete some domains to add a new one, or use wildcard domains instead."); } await projectsCrudHandlers.adminUpdate({ diff --git a/apps/backend/src/app/api/latest/integrations/neon/domains/crud.tsx b/apps/backend/src/app/api/latest/integrations/neon/domains/crud.tsx index d61b2fc7b3..eb6fe8f546 100644 --- a/apps/backend/src/app/api/latest/integrations/neon/domains/crud.tsx +++ b/apps/backend/src/app/api/latest/integrations/neon/domains/crud.tsx @@ -56,7 +56,7 @@ export const domainCrudHandlers = createLazyProxy(() => createCrudHandlers(domai }), onCreate: async ({ auth, data, params }) => { const oldDomains = auth.tenancy.config.domains.trustedDomains; - if (oldDomains.length > 1000) { + if (Object.keys(oldDomains).length > 1000) { throw new StatusError(400, "This project has more than 1000 trusted domains. This is not supported. Please delete some domains to add a new one, or use wildcard domains instead."); } await projectsCrudHandlers.adminUpdate({ From cdbf24cc318fd4357404092d4aa767288110fe6b Mon Sep 17 00:00:00 2001 From: Konstantin Wohlwend Date: Sat, 23 Aug 2025 15:39:05 -0700 Subject: [PATCH 3/6] Limit trusted domains at 300 characters --- packages/stack-shared/src/config/schema.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/stack-shared/src/config/schema.ts b/packages/stack-shared/src/config/schema.ts index d0c27be299..a972b3f6cf 100644 --- a/packages/stack-shared/src/config/schema.ts +++ b/packages/stack-shared/src/config/schema.ts @@ -213,8 +213,8 @@ export const environmentConfigSchema = branchConfigSchema.concat(yupObject({ trustedDomains: yupRecord( userSpecifiedIdSchema("trustedDomainId"), yupObject({ - baseUrl: schemaFields.wildcardUrlSchema, - handlerPath: schemaFields.handlerPathSchema, + baseUrl: schemaFields.wildcardUrlSchema.max(300), + handlerPath: schemaFields.handlerPathSchema.max(300), }), ), })), From 9df0f4d1fcb23f56d90d1f48847ffae81d565ee9 Mon Sep 17 00:00:00 2001 From: Konstantin Wohlwend Date: Sat, 23 Aug 2025 16:04:41 -0700 Subject: [PATCH 4/6] Limit trusted domain size --- .../src/app/api/latest/integrations/custom/domains/crud.tsx | 2 +- .../src/app/api/latest/integrations/neon/domains/crud.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/backend/src/app/api/latest/integrations/custom/domains/crud.tsx b/apps/backend/src/app/api/latest/integrations/custom/domains/crud.tsx index 79b62eed53..88fd292291 100644 --- a/apps/backend/src/app/api/latest/integrations/custom/domains/crud.tsx +++ b/apps/backend/src/app/api/latest/integrations/custom/domains/crud.tsx @@ -8,7 +8,7 @@ import { createLazyProxy } from "@stackframe/stack-shared/dist/utils/proxies"; import { stringCompare } from "@stackframe/stack-shared/dist/utils/strings"; import { projectsCrudHandlers } from "../../../internal/projects/current/crud"; -const domainSchema = schemaFields.urlSchema.defined() +const domainSchema = schemaFields.wildcardUrlSchema.max(300).defined() .matches(/^https?:\/\//, 'URL must start with http:// or https://') .meta({ openapiField: { description: 'URL. Must start with http:// or https://', exampleValue: 'https://example.com' } }); diff --git a/apps/backend/src/app/api/latest/integrations/neon/domains/crud.tsx b/apps/backend/src/app/api/latest/integrations/neon/domains/crud.tsx index eb6fe8f546..cd1320c2ab 100644 --- a/apps/backend/src/app/api/latest/integrations/neon/domains/crud.tsx +++ b/apps/backend/src/app/api/latest/integrations/neon/domains/crud.tsx @@ -8,7 +8,7 @@ import { createLazyProxy } from "@stackframe/stack-shared/dist/utils/proxies"; import { stringCompare } from "@stackframe/stack-shared/dist/utils/strings"; import { projectsCrudHandlers } from "../../../internal/projects/current/crud"; -const domainSchema = schemaFields.urlSchema.defined() +const domainSchema = schemaFields.wildcardUrlSchema.max(300).defined() .matches(/^https?:\/\//, 'URL must start with http:// or https://') .meta({ openapiField: { description: 'URL. Must start with http:// or https://', exampleValue: 'https://example.com' } }); From 071d35854d2ecd44ec9a762bed83742c13cb0706 Mon Sep 17 00:00:00 2001 From: Konstantin Wohlwend Date: Sat, 23 Aug 2025 16:18:11 -0700 Subject: [PATCH 5/6] Limit config size --- apps/backend/src/lib/config.tsx | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/apps/backend/src/lib/config.tsx b/apps/backend/src/lib/config.tsx index 61c47b4c62..ab12cc26c3 100644 --- a/apps/backend/src/lib/config.tsx +++ b/apps/backend/src/lib/config.tsx @@ -1,14 +1,13 @@ import { Prisma } from "@prisma/client"; -import { Config, getInvalidConfigReason, normalize, override } from "@stackframe/stack-shared/dist/config/format"; -import { BranchConfigOverride, BranchConfigOverrideOverride, BranchIncompleteConfig, BranchRenderedConfig, CompleteConfig, EnvironmentConfigOverride, EnvironmentConfigOverrideOverride, EnvironmentIncompleteConfig, EnvironmentRenderedConfig, OrganizationConfigOverride, OrganizationConfigOverrideOverride, OrganizationIncompleteConfig, ProjectConfigOverride, ProjectConfigOverrideOverride, ProjectIncompleteConfig, ProjectRenderedConfig, applyBranchDefaults, applyEnvironmentDefaults, applyOrganizationDefaults, applyProjectDefaults, assertNoConfigOverrideErrors, branchConfigSchema, environmentConfigSchema, getConfigOverrideErrors, getIncompleteConfigWarnings, migrateConfigOverride, organizationConfigSchema, projectConfigSchema, sanitizeBranchConfig, sanitizeEnvironmentConfig, sanitizeOrganizationConfig, sanitizeProjectConfig } from "@stackframe/stack-shared/dist/config/schema"; +import { normalize, override } from "@stackframe/stack-shared/dist/config/format"; +import { BranchConfigOverride, BranchConfigOverrideOverride, BranchRenderedConfig, CompleteConfig, EnvironmentConfigOverride, EnvironmentConfigOverrideOverride, EnvironmentRenderedConfig, OrganizationConfigOverride, ProjectConfigOverride, ProjectConfigOverrideOverride, ProjectRenderedConfig, applyBranchDefaults, applyEnvironmentDefaults, applyOrganizationDefaults, applyProjectDefaults, assertNoConfigOverrideErrors, branchConfigSchema, environmentConfigSchema, migrateConfigOverride, organizationConfigSchema, projectConfigSchema, sanitizeBranchConfig, sanitizeEnvironmentConfig, sanitizeOrganizationConfig, sanitizeProjectConfig } from "@stackframe/stack-shared/dist/config/schema"; import { ProjectsCrud } from "@stackframe/stack-shared/dist/interface/crud/projects"; -import { yupBoolean, yupMixed, yupObject, yupRecord, yupString, yupUnion } from "@stackframe/stack-shared/dist/schema-fields"; +import { yupMixed, yupObject } from "@stackframe/stack-shared/dist/schema-fields"; import { isTruthy } from "@stackframe/stack-shared/dist/utils/booleans"; import { StackAssertionError } from "@stackframe/stack-shared/dist/utils/errors"; import { filterUndefined, typedEntries } from "@stackframe/stack-shared/dist/utils/objects"; import { Result } from "@stackframe/stack-shared/dist/utils/results"; import { deindent, stringCompare } from "@stackframe/stack-shared/dist/utils/strings"; -import * as yup from "yup"; import { RawQuery, globalPrismaClient, rawQuery } from "../prisma-client"; import { listPermissionDefinitionsFromConfig } from "./permissions"; import { DEFAULT_BRANCH_ID } from "./tenancies"; @@ -243,6 +242,16 @@ export async function overrideEnvironmentConfigOverride(options: { oldConfig, options.environmentConfigOverrideOverride, ); + + // large configs make our DB slow; let's prevent them early + const newConfigString = JSON.stringify(newConfig); + if (newConfigString.length > 1_000_000) { + captureError("override-environment-config-too-large", new StackAssertionError(`Environment config override for ${options.projectId}/${options.branchId} is ${(newConfigString.length/1_000_000).toFixed(1)}MB long!`)); + } + if (newConfigString.length > 5_000_000) { + throw new StackAssertionError(`Environment config override for ${options.projectId}/${options.branchId} is too large.`); + } + await assertNoConfigOverrideErrors(environmentConfigSchema, newConfig); await globalPrismaClient.environmentConfigOverride.upsert({ where: { From b5840ff0fdf04a0f483b454706c9afbf7150e5db Mon Sep 17 00:00:00 2001 From: Konstantin Wohlwend Date: Sat, 23 Aug 2025 16:19:37 -0700 Subject: [PATCH 6/6] Limit project config override size --- apps/backend/src/lib/config.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/apps/backend/src/lib/config.tsx b/apps/backend/src/lib/config.tsx index ab12cc26c3..96a31929ed 100644 --- a/apps/backend/src/lib/config.tsx +++ b/apps/backend/src/lib/config.tsx @@ -208,6 +208,16 @@ export async function overrideProjectConfigOverride(options: { oldConfig, options.projectConfigOverrideOverride, ); + + // large configs make our DB slow; let's prevent them early + const newConfigString = JSON.stringify(newConfig); + if (newConfigString.length > 1_000_000) { + captureError("override-project-config-too-large", new StackAssertionError(`Project config override for ${options.projectId} is ${(newConfigString.length/1_000_000).toFixed(1)}MB long!`)); + } + if (newConfigString.length > 5_000_000) { + throw new StackAssertionError(`Project config override for ${options.projectId} is too large.`); + } + await assertNoConfigOverrideErrors(projectConfigSchema, newConfig); await globalPrismaClient.project.update({ where: {