-
Notifications
You must be signed in to change notification settings - Fork 454
Project config override size limit #861
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughAdds wildcard URL support with 300-char limits to trusted domains and handler paths. Introduces 1K trusted-domains guard in two domain CRUD endpoints. Adds size guards (1MB warn, 5MB hard-fail) to project/environment config overrides. Minor import surface reduction; no public API signature changes. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant C as Client
participant API as Domain CRUD Handler
participant CFG as Tenancy Config
C->>API: Create Trusted Domain (baseUrl)
API->>API: Validate baseUrl (wildcard URL, max 300)
API->>CFG: Read existing trustedDomains
alt domains > 1000
API-->>C: 400 StatusError (limit exceeded)
else
API->>CFG: Append domain and persist
API-->>C: 200 OK (created)
end
sequenceDiagram
autonumber
participant S as Service Caller
participant OV as Override Function
participant VAL as Validation
participant DB as Storage
S->>OV: Apply project/env config override
OV->>OV: Compute newConfig
OV->>OV: Serialize newConfig (JSON)
alt size > 5MB
OV-->>S: Throw StackAssertionError (too large)
else alt size > 1MB
OV->>OV: captureError (size warning)
OV->>VAL: assertNoConfigOverrideErrors
VAL-->>OV: OK
OV->>DB: Persist override
OV-->>S: Success
else
OV->>VAL: assertNoConfigOverrideErrors
VAL-->>OV: OK
OV->>DB: Persist override
OV-->>S: Success
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This pull request implements size limitations for project configuration overrides to prevent database performance issues caused by large configuration objects. The changes add validation at multiple levels to control configuration size.
- Adds schema-level size limits (300 characters) for trusted domain URLs and handler paths
- Implements runtime size validation for project and environment configuration overrides with warning at 1MB and hard limit at 5MB
- Adds validation to prevent more than 1000 trusted domains per project
Reviewed Changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
File | Description |
---|---|
packages/stack-shared/src/config/schema.ts | Adds 300-character limits to baseUrl and handlerPath fields in trusted domains schema |
apps/backend/src/lib/config.tsx | Implements size validation for project and environment config overrides with logging and error handling |
apps/backend/src/app/api/latest/integrations/neon/domains/crud.tsx | Updates domain schema to use wildcardUrlSchema with 300-char limit and adds 1000-domain validation |
apps/backend/src/app/api/latest/integrations/custom/domains/crud.tsx | Updates domain schema to use wildcardUrlSchema with 300-char limit and adds 1000-domain validation |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
// 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!`)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The magic number 1_000_000 is used for MB conversion but appears multiple times without a named constant. Consider extracting this to a constant like BYTES_PER_MB = 1_000_000
for better maintainability.
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 > BYTES_PER_MB) { | |
captureError("override-project-config-too-large", new StackAssertionError(`Project config override for ${options.projectId} is ${(newConfigString.length/BYTES_PER_MB).toFixed(1)}MB long!`)); |
Copilot uses AI. Check for mistakes.
// 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!`)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The size validation logic for environment config override is duplicated from the project config override. Consider extracting this into a reusable function to reduce code duplication.
Copilot uses AI. Check for mistakes.
@@ -56,6 +56,9 @@ export const domainCrudHandlers = createLazyProxy(() => createCrudHandlers(domai | |||
}), | |||
onCreate: async ({ auth, data, params }) => { | |||
const oldDomains = auth.tenancy.config.domains.trustedDomains; | |||
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."); | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The magic number 1000 for domain limit appears without a named constant. Consider extracting this to a constant like MAX_TRUSTED_DOMAINS = 1000
for better maintainability and consistency.
Copilot uses AI. Check for mistakes.
@@ -57,6 +57,9 @@ export const domainCrudHandlers = createLazyProxy(() => createCrudHandlers(domai | |||
}), | |||
onCreate: async ({ auth, data, params }) => { | |||
const oldDomains = auth.tenancy.config.domains.trustedDomains; | |||
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."); | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The domain count validation logic is duplicated from the neon domains file. Consider extracting this validation into a shared utility function to reduce code duplication.
Copilot uses AI. Check for mistakes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Greptile Summary
This PR implements size limits and validation constraints across the Stack Auth configuration system to prevent database performance issues caused by excessively large configuration overrides. The changes span multiple areas:
Configuration Schema Updates: The core schema in packages/stack-shared/src/config/schema.ts
adds a 300-character maximum length constraint to baseUrl
and handlerPath
fields in trusted domain configurations. This prevents individual URL fields from growing arbitrarily large while still accommodating legitimate use cases.
Config Override Size Limits: The main configuration handler in apps/backend/src/lib/config.tsx
introduces a two-tier size monitoring system for project and environment config overrides. Configurations over 1MB trigger warnings for monitoring purposes, while those over 5MB are rejected entirely to prevent serious performance degradation.
Domain Management Constraints: Both custom and Neon domain integration endpoints now enforce stricter validation by switching from urlSchema
to wildcardUrlSchema
with 300-character limits, and implementing a hard cap of 1000 trusted domains per project. The error messages guide users toward more efficient wildcard domain patterns.
These changes work together as a comprehensive approach to prevent configuration bloat that could slow database operations in the multi-tenant Stack Auth system. The limits are designed to be generous enough for legitimate use cases while preventing abuse or accidental resource exhaustion.
Confidence score: 1/5
- This PR has a critical runtime error that will break functionality when size limits are exceeded
- Score severely lowered due to missing
captureError
import in the main config file, which will cause immediate failures - Domain validation changes and schema updates appear solid, but the core size limiting functionality is broken
4 files reviewed, 1 comment
// 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!`)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: captureError
is not imported but is being called here. This will cause a runtime error.
Review by RecurseML🔍 Review performed on 70b2244..b5840ff ✨ No bugs found, your code is sparkling clean ✅ Files analyzed, no issues (4)• |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (5)
apps/backend/src/lib/config.tsx (3)
212-220
: Harden size guard: use named constants; keep message MB calc single-sourcedFunctionally correct, but the magic numbers repeat and the division factor appears in two places. Use constants to reduce drift and improve readability.
- 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.`); - } + const newConfigString = JSON.stringify(newConfig); + if (newConfigString.length > CONFIG_OVERRIDE_WARN_MB * BYTES_PER_MB) { + captureError( + "override-project-config-too-large", + new StackAssertionError( + `Project config override for ${options.projectId} is ${(newConfigString.length / BYTES_PER_MB).toFixed(1)}MB long!`, + ), + ); + } + if (newConfigString.length > CONFIG_OVERRIDE_HARD_MB * BYTES_PER_MB) { + throw new StackAssertionError(`Project config override for ${options.projectId} is too large.`); + }
256-264
: Duplicate size-check logic between project/env paths — factor into a helperThe environment path repeats the same guard. Extract a small helper to keep the checks consistent and reduce chances of future drift.
- // 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.`); - } + enforceConfigSizeLimits( + "environment", + `${options.projectId}/${options.branchId}`, + newConfig, + );Add this helper once in the module (outside the selected range):
function enforceConfigSizeLimits( scope: "project" | "environment", id: string, cfg: unknown, ) { const s = JSON.stringify(cfg); const warnKey = scope === "project" ? "override-project-config-too-large" : "override-environment-config-too-large"; if (s.length > CONFIG_OVERRIDE_WARN_MB * BYTES_PER_MB) { captureError(warnKey, new StackAssertionError(`${scope} config override for ${id} is ${(s.length / BYTES_PER_MB).toFixed(1)}MB long!`)); } if (s.length > CONFIG_OVERRIDE_HARD_MB * BYTES_PER_MB) { throw new StackAssertionError(`${scope} config override for ${id} is too large.`); } }Happy to wire the project path to this helper too for symmetry.
1-8
: Missing import for captureError + propose central constants for size math
captureError(...)
is used later but not imported. Also, the MB math literals appear multiple times; pull them into named constants for clarity and consistency.Apply this diff to import the missing symbol:
-import { StackAssertionError } from "@stackframe/stack-shared/dist/utils/errors"; +import { StackAssertionError, captureError } from "@stackframe/stack-shared/dist/utils/errors";Optionally add shared constants near the imports:
// bytes/limits for config override size checks const BYTES_PER_MB = 1_000_000; const CONFIG_OVERRIDE_WARN_MB = 1; // warn at 1MB const CONFIG_OVERRIDE_HARD_MB = 5; // hard-fail at 5MBapps/backend/src/app/api/latest/integrations/neon/domains/crud.tsx (1)
59-61
: Off-by-one: cap at 1000 should block when count is already 1000 (use >=)As written, a project with exactly 1000 domains can still add a 1001st. Fix the predicate and avoid magic numbers with a named constant.
- 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."); - } + // Enforce hard cap of 1000 domains + if (Object.keys(oldDomains).length >= MAX_TRUSTED_DOMAINS) { + throw new StatusError( + 400, + `This project already has ${MAX_TRUSTED_DOMAINS} trusted domains. Delete some or use wildcard domains instead.` + ); + }Add near the top of the file:
const MAX_TRUSTED_DOMAINS = 1000;Follow-up options:
- Extract this check into a small shared util to de-duplicate custom/neon handlers.
- Optionally reject duplicate domains on create to avoid unnecessary consumption of the cap.
Would you like me to propose a tiny shared helper and wire both handlers?
apps/backend/src/app/api/latest/integrations/custom/domains/crud.tsx (1)
60-62
: Off-by-one: block creation when count is already 1000Use >= and a shared constant to match the intended cap. This logic is duplicated across integrations; consider extracting.
- 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."); - } + if (Object.keys(oldDomains).length >= MAX_TRUSTED_DOMAINS) { + throw new StatusError( + 400, + `This project already has ${MAX_TRUSTED_DOMAINS} trusted domains. Delete some or use wildcard domains instead.` + ); + }Add near the top:
const MAX_TRUSTED_DOMAINS = 1000;If you want, I can draft a tiny helper (e.g., enforceTrustedDomainLimit(oldDomains)) and use it in both files.
🧹 Nitpick comments (3)
packages/stack-shared/src/config/schema.ts (1)
216-218
: LGTM: 300-char caps on baseUrl/handlerPathThe max(300) aligns with the CRUD input schema. Keeps oversized entries out at the schema layer.
Optional: consider centralizing 300 as a shared constant (e.g., in schema-fields) to keep CRUD and schema in lockstep and avoid drift.
apps/backend/src/app/api/latest/integrations/neon/domains/crud.tsx (1)
11-13
: Domain schema upgrade looks goodSwitch to wildcardUrlSchema with 300-char cap and explicit http(s) check is consistent with the PR objective.
If wildcardUrlSchema already enforces http(s), the additional .matches may be redundant. Not a blocker.
apps/backend/src/app/api/latest/integrations/custom/domains/crud.tsx (1)
11-13
: Domain schema upgrade looks goodWildcards + 300-char limit matches the Neon handler and shared schema.
Same note as Neon: the explicit http(s) regex may be redundant if wildcardUrlSchema already guarantees it.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
apps/backend/src/app/api/latest/integrations/custom/domains/crud.tsx
(2 hunks)apps/backend/src/app/api/latest/integrations/neon/domains/crud.tsx
(2 hunks)apps/backend/src/lib/config.tsx
(3 hunks)packages/stack-shared/src/config/schema.ts
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
apps/backend/src/app/api/latest/**
📄 CodeRabbit inference engine (CLAUDE.md)
Place backend API route handlers under /apps/backend/src/app/api/latest and follow RESTful, resource-based paths (auth, users, teams, oauth-providers)
Files:
apps/backend/src/app/api/latest/integrations/custom/domains/crud.tsx
apps/backend/src/app/api/latest/integrations/neon/domains/crud.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Prefer ES6 Map over Record where feasible
Files:
apps/backend/src/app/api/latest/integrations/custom/domains/crud.tsx
apps/backend/src/app/api/latest/integrations/neon/domains/crud.tsx
packages/stack-shared/src/config/schema.ts
apps/backend/src/lib/config.tsx
🧬 Code graph analysis (3)
apps/backend/src/app/api/latest/integrations/custom/domains/crud.tsx (1)
packages/stack-shared/src/utils/errors.tsx (1)
StatusError
(152-261)
apps/backend/src/app/api/latest/integrations/neon/domains/crud.tsx (1)
packages/stack-shared/src/utils/errors.tsx (1)
StatusError
(152-261)
apps/backend/src/lib/config.tsx (1)
packages/stack-shared/src/utils/errors.tsx (2)
captureError
(126-134)StackAssertionError
(69-85)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: build (22.x)
- GitHub Check: restart-dev-and-test
- GitHub Check: setup-tests
- GitHub Check: build (22.x)
- GitHub Check: docker
- GitHub Check: lint_and_build (latest)
- GitHub Check: docker
- GitHub Check: all-good
- GitHub Check: Security Check
onCreate: async ({ auth, data, params }) => { | ||
const oldDomains = auth.tenancy.config.domains.trustedDomains; | ||
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({ | ||
data: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Optional: verify both handlers updated consistently
Quick grep to ensure we fix all occurrences and use >= with the shared constant.
🏁 Script executed:
#!/bin/bash
# Expect 2 matches, both using '>= MAX_TRUSTED_DOMAINS'
rg -nP --glob 'apps/backend/src/app/api/latest/**/domains/crud.tsx' -C2 'Object\.keys\(\s*oldDomains\s*\)\.length\s*[<>]=?\s*(MAX_TRUSTED_DOMAINS|1000)'
Length of output: 1581
Use shared MAX_TRUSTED_DOMAINS constant and “≥” in both domain‐count checks
Both the Custom and Neon integration handlers are still using a hard-coded > 1000
check rather than the shared MAX_TRUSTED_DOMAINS
constant with a “≥” operator. Please update each occurrence:
• apps/backend/src/app/api/latest/integrations/custom/domains/crud.tsx (line 60)
Change
if (Object.keys(oldDomains).length > 1000) {
to
if (Object.keys(oldDomains).length >= MAX_TRUSTED_DOMAINS) {
• apps/backend/src/app/api/latest/integrations/neon/domains/crud.tsx (line 59)
Change
if (Object.keys(oldDomains).length > 1000) {
to
if (Object.keys(oldDomains).length >= MAX_TRUSTED_DOMAINS) {
Also ensure you import the shared constant at the top of each file, for example:
import { MAX_TRUSTED_DOMAINS } from '../../../shared/constants';
🤖 Prompt for AI Agents
In apps/backend/src/app/api/latest/integrations/neon/domains/crud.tsx around
lines 57 to 63, replace the hard-coded domain limit check using `> 1000` with
the shared MAX_TRUSTED_DOMAINS constant and the greater-or-equal operator:
change the length check to use `>= MAX_TRUSTED_DOMAINS`, and add an import for
MAX_TRUSTED_DOMAINS from the shared constants module at the top of the file
(adjust relative path as needed).
Important
Enforce size limits on project/environment config overrides and trusted domains to prevent performance issues.
onCreate
incustom/domains/crud.tsx
andneon/domains/crud.tsx
.baseUrl
andhandlerPath
to 300 characters inenvironmentConfigSchema
inschema.ts
.overrideProjectConfigOverride
andoverrideEnvironmentConfigOverride
inconfig.tsx
.urlSchema
towildcardUrlSchema
with a max length of 300 incustom/domains/crud.tsx
andneon/domains/crud.tsx
.config.tsx
.This description was created by
for b5840ff. You can customize this summary. It will automatically update as commits are pushed.
Summary by CodeRabbit