You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat: add zod validation to internal and external endpoints - #612
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR introduces centralized Zod schemas and validation helpers, then applies them across multiple Next.js API routes to harden user-input handling and upstream-response parsing.
Changes:
Added reusable Zod schema modules under lib/api/schemas/* plus shared validation helpers in lib/api/errors.ts.
Updated a broad set of API routes to validate request inputs, external API responses, and/or endpoint outputs before returning JSON.
Added a new /api/totalTokenSupply endpoint backed by a subgraph query with response validation.
Reviewed changes
Copilot reviewed 37 out of 38 changed files in this pull request and generated 12 comments.
Show a summary per file
File
Description
pages/api/usage.tsx
Uses centralized usage schemas and validates output; adds guardrails for insufficient data.
pages/api/upload-ipfs.tsx
Validates POST body and Pinata response; validates output shape.
pages/api/treasury/votes/[address]/registered.tsx
Validates address input and response output for registered-to-vote.
ENS schema accepts malformed names before normalization
lib/api/schemas/ens.ts:56
This schema accepts any non-empty string, including malformed ENS names such as values containing spaces. Those inputs pass validateInput, then normalize() throws and the route returns 404 instead of the intended 400. Incorporate ENS normalization into the schema (and apply the blacklist to the normalized name) so malformed names are rejected before the fetch path.
Proposal ID schema allows values beyond uint256
lib/api/schemas/treasury.ts:14
The numeric regex accepts values larger than uint256, but both callers immediately ABI-encode the result as a proposal ID. A value of 2^256 or greater therefore passes input validation and fails later as a 500 instead of a 400. Add the ABI range check to this shared schema.
Payload limit counts UTF-16 units instead of UTF-8 bytes
lib/api/schemas/upload-ipfs.ts:11
z.string().max(500000) limits UTF-16 code units, not payload bytes, so this does not enforce the documented 500KB abuse limit. For example, 200,000 emoji pass this check at 400,000 code units but occupy 800,000 UTF-8 bytes. Enforce the UTF-8 byte length (and use a body-parser limit if the limit is intended for the entire JSON request).
Unvalidated upstream project data causes incorrect responses
pages/api/changefeed.tsx:80
projectBySlugs is still data supplied by changefeed.app, but it is passed to validateOutput. Consequently a malformed upstream payload becomes a 500 in development and is returned with 200 in production. Validate it with validateExternalResponse and return externalApiError (502) on failure, consistent with the other external-service endpoints.
Case-sensitive address comparison reports zero price
pages/api/score/index.tsx:177
The metrics record key may be a valid checksummed address, but only the pricing address is lowercased here. In that case the same address does not match and this endpoint reports pricePerPixel: 0. Compare both sides case-insensitively, as the single-address endpoint already does.
The reason will be displayed to describe this comment to others. Learn more.
@ECWireless Thanks for this. It's a big PR, so I'd trim it to where zod actually protects something.
Keep zod where data crosses a trust boundary:
user input (address and id params, generateProof, upload-ipfs body)
third-party responses (Pinata, subgraph, metrics and pricing servers, ENS records)
Remove output validation on responses we build ourselves from typed values (current-round, contracts, account-balance, l1-delegator, pending-stake, treasury routes, generateProof, upload-ipfs, usage). It can't fail there, and validateOutput only errors in development anyway.
Also:
Drop changefeed (removed in #838) and these unused schemas: OptionalStringSchema, EnsAvatarResultSchema, EnsAvatarProviderSchema, SubgraphResponseSchema, CurrentRoundSubgraphResultSchema, TreasuryProposalStateSchema, TreasuryVoteSchema.
usage can go too unless the chart is coming back soon (hidden in #825).
Once trimmed, consider one schema file per route (pending-stake.ts, generate-proof.ts, ...) with common.ts for shared primitives only, all exported from index.ts and imported only from @lib/api/schemas.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See #521 for all details. Original PR created by @Roaring30s .