Skip to content

fix: AI safety limits and env validation#713

Merged
2witstudios merged 3 commits intomasterfrom
ppg/ai-safety-env-validation
Feb 25, 2026
Merged

fix: AI safety limits and env validation#713
2witstudios merged 3 commits intomasterfrom
ppg/ai-safety-env-validation

Conversation

@2witstudios
Copy link
Owner

@2witstudios 2witstudios commented Feb 24, 2026

Summary

  • Cap nested agent tool explosion: Reduced nested stepCountIs from 100 to 20 and MAX_AGENT_DEPTH from 3 to 2, preventing potential 10,000 tool execution chains
  • Env validation at startup: validateEnv() now runs in instrumentation.ts on server boot; added 5 missing env vars to schema (OAUTH_STATE_SECRET, APPLE_SERVICE_ID, REALTIME_BROADCAST_SECRET, CRON_SECRET, COOKIE_DOMAIN) with .min(1).optional() to reject empty strings
  • Safe JSON body parsing: New safeParseBody() utility returns 400 with string error message for malformed JSON instead of 500; applied to /api/drives and /api/storage/check routes

Review fixes

  • safeParseBody now returns flattened string error messages (not Zod issues array), matching existing { error: "string" } API convention
  • Drives route uses z.preprocess to produce consistent 'Missing name' error for both missing field and empty string
  • Schemas hoisted to module-level constants
  • Optional secrets reject empty strings via .min(1).optional()

Test plan

  • parse-body.test.ts — 5 tests: valid JSON, malformed JSON, schema failure (string error), empty body, extra fields
  • env-validation.test.ts — 13 existing tests still passing with new schema fields
  • agent-communication-tools.test.ts — depth limit test updated for new MAX_AGENT_DEPTH=2
  • Manual: verify pnpm dev starts without env validation errors in development
  • Manual: send malformed JSON to POST /api/drives and confirm 400 response

🤖 Generated with Claude Code

- Reduce nested agent stepCountIs from 100 to 20 and MAX_AGENT_DEPTH from 3 to 2
  to prevent step explosion (100×100 = 10k tool executions)
- Call validateEnv() in instrumentation.ts to catch env misconfig at startup
- Add missing env vars to validation schema (OAUTH_STATE_SECRET, APPLE_SERVICE_ID,
  REALTIME_BROADCAST_SECRET, CRON_SECRET, COOKIE_DOMAIN)
- Export env validation functions from @pagespace/lib/server
- Add safeParseBody() utility for safe request.json() with Zod validation
- Update drives and storage/check routes to return 400 on malformed JSON

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 24, 2026

📝 Walkthrough

Walkthrough

This pull request introduces structured input validation across API routes using a new safeParseBody utility backed by Zod schemas, adds runtime environment validation at server startup, extends the environment variable schema with five optional fields, and reduces agent communication nesting depth limits from 3 to 2 with corresponding test adjustments.

Changes

Cohort / File(s) Summary
API Route Validation Refactoring
apps/web/src/app/api/drives/route.ts, apps/web/src/app/api/storage/check/route.ts
Replaces manual JSON extraction and inline validation with safeParseBody utility using Zod schemas. Drives route validates drive name; storage check route validates fileSize as positive number. Parser-provided error responses replace custom messages.
Validation Utility Implementation
apps/web/src/lib/validation/parse-body.ts, apps/web/src/lib/validation/__tests__/parse-body.test.ts
Introduces new safeParseBody<T> utility function for generic JSON body parsing and Zod schema validation. Returns structured result with success/failure states. Test file covers valid JSON, malformed JSON, schema validation failures, empty bodies, and extra field stripping.
Environment Configuration
packages/lib/src/config/env-validation.ts, packages/lib/src/server.ts
Adds five optional environment variables (OAUTH_STATE_SECRET, APPLE_SERVICE_ID, REALTIME_BROADCAST_SECRET, CRON_SECRET, COOKIE_DOMAIN) to server schema. Re-exports validation utilities (validateEnv, getEnvErrors, isEnvValid, getValidatedEnv) from server entry point.
Runtime Validation & Instrumentation
apps/web/src/instrumentation.ts
Adds environment validation at server startup within register() function, conditioned on NEXT_RUNTIME === 'nodejs'. Dynamically imports validateEnv and logs success before initializing existing activity/workflow hooks.
Agent Communication Depth Limits
apps/web/src/lib/ai/tools/agent-communication-tools.ts, apps/web/src/lib/ai/tools/__tests__/agent-communication-tools.test.ts
Reduces MAX_AGENT_DEPTH from 3 to 2 and lowers ask_agent workflow step count limit from 100 to 20, preventing deeper nesting in agent chains. Updates corresponding test expectation from agentCallDepth 3 to 2.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • PageSpace#78: Updates agent-communication-tools tests and may relate to the MAX_AGENT_DEPTH reduction and step count adjustments in this PR.
  • PageSpace#158: Likely uses CRON_SECRET environment variable which is newly added to the validation schema in this PR.

Poem

🐰 Validation schemas guard each gate,
Zod and parse make data great,
Environment secrets safely store,
Agent chains nest less—but that's no chore!
The rabbit hops through cleaner code,
With safety checks along the road! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the primary changes in the PR: AI safety limits and environment validation, which are the core themes across all file modifications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ppg/ai-safety-env-validation

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0084079de4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if (!result.success) {
return {
success: false,
response: Response.json({ error: result.error.issues }, { status: 400 }),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve validation error contract for API responses

Returning result.error.issues here changes error from a string to an array of Zod issue objects for every schema failure (for example, POST /api/drives with a missing/empty name). Existing route contracts in this repo (e.g. src/app/api/drives/__tests__/route.test.ts) expect a string error payload, so this introduces a compatibility regression for clients/tests that render or compare error as text.

Useful? React with 👍 / 👎.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 96515a0. safeParseBody now flattens Zod issues into a semicolon-joined string, matching the { error: "string" } contract used across all existing routes. The drives schema also uses z.preprocess to normalize missing/empty name to the same "Missing name" error message.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
apps/web/src/app/api/drives/route.ts (1)

62-64: Same Zod v4 error-option concern as storage/check schema.

z.string().min(1, 'Missing name') may need the { error: ... } options object in v4; apply the same fix here if required.

🛠️ Proposed fix
-  name: z.string().min(1, 'Missing name'),
+  name: z.string().min(1, { error: 'Missing name' }),
In Zod v4, does z.string().min accept a string message argument, or must it use the { error: ... } options object?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/api/drives/route.ts` around lines 62 - 64, createDriveSchema
currently uses z.string().min(1, 'Missing name') which is incompatible with Zod
v4; change the second argument to an options object like z.string().min(1, {
message: 'Missing name' }) (same pattern used for the storage/check schema) so
the error message is passed correctly to the zod validator.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/lib/validation/parse-body.ts`:
- Around line 25-30: safeParseBody currently returns result.error.issues (an
array) from the schema.safeParse call which diverges from the established
validation shape; change the error response to use
result.error.flatten().fieldErrors so the response.body follows the same {
field: [messages] } pattern used across routes, and update the corresponding
test expectation (the test asserting an array at the failing case) to expect an
object of fieldErrors instead; look for the safeParseBody function and its
Response.json({...}) return and replace result.error.issues with
result.error.flatten().fieldErrors, then adjust the test that asserts the error
shape to expect an object.

In `@packages/lib/src/config/env-validation.ts`:
- Around line 51-62: The optional secret/token schemas currently use
z.string().optional() which permits empty strings; update each to require at
least one character by changing OAUTH_STATE_SECRET, APPLE_SERVICE_ID,
STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, REALTIME_BROADCAST_SECRET,
CRON_SECRET, and COOKIE_DOMAIN from z.string().optional() to
z.string().min(1).optional() so empty secrets are rejected (consistent with
DATABASE_URL and the existing superRefine checks for
CSRF_SECRET/ENCRYPTION_KEY).

---

Duplicate comments:
In `@apps/web/src/app/api/drives/route.ts`:
- Around line 62-64: createDriveSchema currently uses z.string().min(1, 'Missing
name') which is incompatible with Zod v4; change the second argument to an
options object like z.string().min(1, { message: 'Missing name' }) (same pattern
used for the storage/check schema) so the error message is passed correctly to
the zod validator.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6247d21 and 0084079.

📒 Files selected for processing (9)
  • apps/web/src/app/api/drives/route.ts
  • apps/web/src/app/api/storage/check/route.ts
  • apps/web/src/instrumentation.ts
  • apps/web/src/lib/ai/tools/__tests__/agent-communication-tools.test.ts
  • apps/web/src/lib/ai/tools/agent-communication-tools.ts
  • apps/web/src/lib/validation/__tests__/parse-body.test.ts
  • apps/web/src/lib/validation/parse-body.ts
  • packages/lib/src/config/env-validation.ts
  • packages/lib/src/server.ts

2witstudios and others added 2 commits February 24, 2026 16:42
- safeParseBody now returns flattened string error messages instead of
  raw Zod issues array, matching existing { error: 'string' } API convention
- Drives route uses z.preprocess to produce consistent 'Missing name'
  error for both missing field and empty string cases
- Hoisted Zod schemas to module level in drives and storage/check routes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add .min(1) to all optional secret/token env var schemas to catch
misconfiguration from empty values (e.g., CRON_SECRET= in .env).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@2witstudios 2witstudios merged commit 28d0ecd into master Feb 25, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant