Skip to content

Conversation

@IFAKA
Copy link

@IFAKA IFAKA commented Dec 16, 2025

Summary

Adds opt-in security features to protect against common web application vulnerabilities:

  • mode: Control how unknown keys are handled ('strip' | 'strict' | 'passthrough')
  • trim: Automatically trim whitespace from strings and enums
  • defaultTextMaxLength: Limit unbounded text columns to prevent DoS attacks

When using createSchemaFactory(), secure defaults are applied automatically. Standalone functions remain backward compatible.

Motivation

Mass Assignment Attacks

Without strict mode, attackers can inject extra fields like isAdmin: true:

// Vulnerable: unknown keys are silently stripped
const schema = createInsertSchema(users);
schema.parse({ name: "hacker", isAdmin: true }); // isAdmin passes through!

// Protected: strict mode rejects unknown keys
const { createInsertSchema } = createSchemaFactory({ mode: 'strict' });

Whitespace Bypass Attacks

Attackers can bypass validation with whitespace:

// Vulnerable: " admin " !== "admin"
const schema = createInsertSchema(users);

// Protected: trim normalizes input
const { createInsertSchema } = createSchemaFactory({ trim: true });

DoS via Large Payloads

Unbounded text columns can accept gigabytes of data:

// Vulnerable: no limit on text size
const schema = createInsertSchema(posts);

// Protected: default 64KB limit
const { createInsertSchema } = createSchemaFactory({ defaultTextMaxLength: 65535 });

Secure Defaults

createSchemaFactory() now applies secure defaults:

  • mode: 'strict' - Reject unknown keys
  • trim: true - Trim whitespace
  • defaultTextMaxLength: 65535 - 64KB limit

Users can override any default:

const { createInsertSchema } = createSchemaFactory({
  mode: 'strip',  // opt-out of strict
  defaultTextMaxLength: 1024 * 1024,  // 1MB limit
});

Backward Compatibility

  • Standalone createInsertSchema(), createSelectSchema(), createUpdateSchema() are unchanged
  • Only createSchemaFactory() applies secure defaults
  • All defaults can be overridden

Changes

  • src/schema.types.ts: Add SchemaMode, security options to factory
  • src/schema.ts: Add applySchemaMode(), secure defaults
  • src/column.ts: Add trim/maxLength support with z.preprocess()
  • tests/pg.test.ts: Add 10 comprehensive security tests

Test plan

  • All 80 tests pass (70 existing + 10 new)
  • Mode strict/strip/passthrough verified
  • Trim works on strings and enums
  • defaultTextMaxLength limits unbounded text
  • Columns with explicit length are not affected
  • Standalone functions unchanged (backward compatible)

- Add mode option: 'strip' (default), 'strict', 'passthrough' for unknown key handling
- Add trim option: applies whitespace trimming to strings and enums via preprocess
- Add defaultTextMaxLength option: limits unbounded text columns (DoS protection)
- Add secure defaults for createSchemaFactory (strict mode, trim, 65535 max length)
- Standalone functions remain backward compatible (no secure defaults)
- Add 10 comprehensive security tests
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