UNPKG

alepha

Version:

Easy-to-use modern TypeScript framework for building many kind of applications.

272 lines (213 loc) 7.46 kB
import { Alepha, z } from "alepha"; import { describe, test } from "vitest"; import { SchemaValidator } from "../providers/SchemaValidator.ts"; describe("SchemaValidator", () => { describe("Basic validation", () => { test("should validate simple objects", async ({ expect }) => { const alepha = Alepha.create(); const validator = alepha.inject(SchemaValidator); const schema = z.object({ name: z.text(), age: z.integer(), }); const data = { name: "Alice", age: 30, }; const result = validator.validate(schema, data); expect(result).toEqual(data); }); test("should trim strings when schema has trim option", async ({ expect, }) => { const alepha = Alepha.create(); const validator = alepha.inject(SchemaValidator); const schema = z.object({ name: z.text({ trim: true }), }); const data = { name: " Alice ", }; const result = validator.validate(schema, data); expect(result.name).toBe("Alice"); }); test("rejects null for an optional (non-nullable) field (strict)", async ({ expect, }) => { const alepha = Alepha.create(); const validator = alepha.inject(SchemaValidator); const schema = z.object({ name: z.text(), bio: z.text().optional(), }); // Standard zod is strict: an optional field accepts `undefined`, not // `null` (the old `nullToUndefined` coercion has been dropped). expect(() => validator.validate(schema, { name: "Alice", bio: null }), ).toThrow(); const result = validator.validate(schema, { name: "Alice" }); expect(result.name).toBe("Alice"); expect(result.bio).toBeUndefined(); }); }); describe("Security - Prototype Pollution Protection", () => { test("should filter out __proto__ key from input data", async ({ expect, }) => { const alepha = Alepha.create(); const validator = alepha.inject(SchemaValidator); const schema = z.object({ name: z.text(), }); // Input data with __proto__ key via JSON.parse (bypasses literal protection) const data = JSON.parse('{"name":"Alice","__proto__":{"isAdmin":true}}'); const result = validator.validate(schema, data); // __proto__ should not be an own property in the result expect(result.name).toBe("Alice"); // Using hasOwnProperty because 'in' operator has special behavior for __proto__ expect(Object.hasOwn(result, "__proto__")).toBe(false); }); test("should filter out constructor key from input data", async ({ expect, }) => { const alepha = Alepha.create(); const validator = alepha.inject(SchemaValidator); const schema = z.object({ name: z.text(), }); const data = { name: "Alice", constructor: { prototype: { isAdmin: true } }, }; const result = validator.validate(schema, data); expect(result.name).toBe("Alice"); expect(Object.hasOwn(result, "constructor")).toBe(false); }); test("should filter out prototype key from input data", async ({ expect, }) => { const alepha = Alepha.create(); const validator = alepha.inject(SchemaValidator); const schema = z.object({ name: z.text(), }); const data = { name: "Alice", prototype: { isAdmin: true }, }; const result = validator.validate(schema, data); expect(result.name).toBe("Alice"); expect(Object.hasOwn(result, "prototype")).toBe(false); }); test("should filter unsafe keys from nested objects", async ({ expect, }) => { const alepha = Alepha.create(); const validator = alepha.inject(SchemaValidator); const schema = z.object({ user: z.object({ name: z.text(), }), }); const data = { user: { name: "Alice", __proto__: { isAdmin: true }, }, }; const result = validator.validate(schema, data); expect(result.user.name).toBe("Alice"); expect(Object.hasOwn(result.user, "__proto__")).toBe(false); }); test("should not pollute Object.prototype", async ({ expect }) => { const alepha = Alepha.create(); const validator = alepha.inject(SchemaValidator); const schema = z.object({ name: z.text(), }); // Attempt to pollute Object.prototype via __proto__ const maliciousData = JSON.parse( '{"name":"Alice","__proto__":{"polluted":"yes"}}', ); validator.validate(schema, maliciousData); // Object.prototype should not be polluted expect(({} as any).polluted).toBeUndefined(); }); test("should create objects without prototype chain", async ({ expect, }) => { const alepha = Alepha.create(); const validator = alepha.inject(SchemaValidator); const schema = z.object({ name: z.text(), }); const data = { name: "Alice", }; const result = validator.validate(schema, data); // The result should not have Object.prototype methods directly accessible // via hasOwnProperty (it should use Object.create(null)) expect(result.name).toBe("Alice"); }); }); describe("beforeParse", () => { test("should handle arrays correctly", async ({ expect }) => { const alepha = Alepha.create(); const validator = alepha.inject(SchemaValidator); const schema = z.object({ tags: z.array(z.text({ trim: true })), }); const data = { tags: [" tag1 ", " tag2 "], }; const result = validator.validate(schema, data); expect(result.tags).toEqual(["tag1", "tag2"]); }); test("should unwrap optional arrays and preprocess items", async ({ expect, }) => { const alepha = Alepha.create(); const validator = alepha.inject(SchemaValidator); const schema = z.object({ tags: z.array(z.text({ trim: true })).optional(), }); const data = { tags: [" tag1 ", " tag2 "], }; const result = validator.validate(schema, data); expect(result.tags).toEqual(["tag1", "tag2"]); }); test("should unwrap nullable arrays and preprocess items", async ({ expect, }) => { const alepha = Alepha.create(); const validator = alepha.inject(SchemaValidator); const schema = z.object({ tags: z.union([z.array(z.text({ trim: true })), z.null()]), }); const data = { tags: [" tag1 ", " tag2 "], }; const result = validator.validate(schema, data); expect(result.tags).toEqual(["tag1", "tag2"]); }); test("validates an optional field left undefined (strict)", async ({ expect, }) => { const alepha = Alepha.create(); const validator = alepha.inject(SchemaValidator); const schema = z.object({ name: z.text(), bio: z.text().optional(), }); // The `deleteUndefined` option has been dropped; an undefined optional // field simply validates to `undefined`. const result = validator.validate(schema, { name: "Alice", bio: undefined, }); expect(result.name).toBe("Alice"); expect(result.bio).toBeUndefined(); }); }); });