fix(housekeeping): close schema reflection escapes
CI / check (pull_request) Successful in 30s
CI / release (pull_request) Skipped
CI / deploy (pull_request) Skipped

This commit is contained in:
Simo committed 2026-08-27 20:31:10 +02:00
1 parent 7895188b56
commit 01dceac073
3 files changed
+539 -37

No files matched your search

@@ -25,6 +25,52 @@ function command(
};
}
type DescriptorReader = (
target: object,
property: PropertyKey,
) => PropertyDescriptor | undefined;
const descriptorReaders = [
[
"Object.getOwnPropertyDescriptor",
"object-descriptor",
Object.getOwnPropertyDescriptor,
],
[
"Object.getOwnPropertyDescriptors",
"object-descriptors",
(target: object, property: PropertyKey) =>
Reflect.get(Object.getOwnPropertyDescriptors(target), property) as
| PropertyDescriptor
| undefined,
],
[
"Reflect.getOwnPropertyDescriptor",
"reflect-descriptor",
Reflect.getOwnPropertyDescriptor,
],
] as const satisfies readonly (readonly [string, string, DescriptorReader])[];
function readDescriptorValue(
target: object,
property: PropertyKey,
readDescriptor: DescriptorReader,
): unknown {
const descriptor = readDescriptor(target, property);
if (!descriptor) throw new Error(`missing descriptor: ${String(property)}`);
if ("value" in descriptor) return descriptor.value;
if (descriptor.get) return Reflect.apply(descriptor.get, target, []);
return undefined;
}
function attemptMutation(mutate: () => void): void {
try {
mutate();
} catch {
// Readonly public views may reject the mutation directly.
}
}
describe("housekeeping command registry", () => {
it("returns the validated snapshot registered under its global ID", () => {
const registered = command("people.registry.lookup");
@@ -296,6 +342,170 @@ describe("housekeeping command registry", () => {
});
expect(String(thrown)).not.toContain("caller lazy secret");
});
it.each(descriptorReaders)(
"keeps private validation unchanged after nested mutation through %s",
(_api, suffix, readDescriptor) => {
const input = z.object({ value: z.string().min(3) });
const id = `people.registry.${suffix}`;
registerHousekeepingCommand({
...command(id),
input,
execute: async (context) => ok({ id: 1 }, context.correlationId),
} as HousekeepingCommand<z.output<typeof input>, { id: number }>);
const registered = getHousekeepingCommand(id);
if (!registered) throw new Error("registered command missing");
const definition = readDescriptorValue(
registered.input,
"def",
readDescriptor,
) as object;
const shape = readDescriptorValue(
definition,
"shape",
readDescriptor,
) as { value: z.ZodType };
const child = shape.value;
const childDefinition = readDescriptorValue(
child,
"def",
readDescriptor,
) as object;
const checks = readDescriptorValue(
childDefinition,
"checks",
readDescriptor,
) as readonly object[];
const internal = readDescriptorValue(
checks[0] as object,
"_zod",
readDescriptor,
) as object;
const checkDefinition = readDescriptorValue(
internal,
"def",
readDescriptor,
) as { minimum: number };
attemptMutation(() => {
shape.value = z.number();
});
attemptMutation(() => {
checkDefinition.minimum = 0;
});
expect(registered.input.safeParse({ value: "ab" }).success).toBe(false);
expect(registered.input.safeParse({ value: "abcd" }).success).toBe(true);
expect(registered.input.safeParse({ value: 7 }).success).toBe(false);
},
);
it("keeps own-key and symbol iteration detached from private checks", () => {
const input = z.object({ value: z.string().min(3) });
const id = "people.registry.symbol-iteration";
registerHousekeepingCommand({
...command(id),
input,
execute: async (context) => ok({ id: 1 }, context.correlationId),
} as HousekeepingCommand<z.output<typeof input>, { id: number }>);
const registered = getHousekeepingCommand(id);
if (!registered) throw new Error("registered command missing");
const publicInput = registered.input as z.ZodObject<{
value: z.ZodString;
}>;
const shape = publicInput.shape;
expect(Reflect.ownKeys(publicInput.def)).toContain("shape");
expect(Object.keys(shape)).toEqual(["value"]);
const child = Object.entries(shape)[0]?.[1];
if (!child) throw new Error("public child schema missing");
const checks = child.def.checks ?? [];
const spreadChecks = [...checks];
const iterator = checks[Symbol.iterator]();
const iteratedCheck = iterator.next().value;
if (!iteratedCheck) throw new Error("public check missing");
expect(spreadChecks[0]).toBe(iteratedCheck);
attemptMutation(() => {
(
iteratedCheck as unknown as {
_zod: { def: { minimum: number } };
}
)._zod.def.minimum = 0;
});
expect(registered.input.safeParse({ value: "ab" }).success).toBe(false);
expect(registered.input.safeParse({ value: "abcd" }).success).toBe(true);
});
it("keeps prototype methods operational without passing the private schema to callbacks", async () => {
const input = z.object({ value: z.string().min(3) });
const id = "people.registry.prototype-methods";
registerHousekeepingCommand({
...command(id),
input,
execute: async (context) => ok({ id: 1 }, context.correlationId),
} as HousekeepingCommand<z.output<typeof input>, { id: number }>);
const registered = getHousekeepingCommand(id);
if (!registered) throw new Error("registered command missing");
const publicInput = registered.input as z.ZodObject<{
value: z.ZodString;
}>;
const publicPrototype = Object.getPrototypeOf(publicInput);
expect(publicPrototype).toBe(Reflect.getPrototypeOf(publicInput));
const prototypeMutation = Symbol("prototype-mutation");
Object.defineProperty(publicPrototype, prototypeMutation, {
configurable: true,
value(this: z.ZodObject<{ value: z.ZodString }>): unknown {
attemptMutation(() => {
this.def.shape.value = z.number() as never;
});
return this.def;
},
});
try {
(
publicInput as typeof publicInput & {
[prototypeMutation](): unknown;
}
)[prototypeMutation]();
} finally {
Reflect.deleteProperty(publicPrototype, prototypeMutation);
}
expect(publicInput.safeParse({ value: "abcd" }).success).toBe(true);
expect(publicInput.safeParse({ value: 7 }).success).toBe(false);
let appliedSchema: z.ZodType | undefined;
const applied = publicInput.apply((schema) => {
appliedSchema = schema;
return schema;
});
expect(appliedSchema).toBe(publicInput);
expect(applied).toBe(publicInput);
let externallyRegistered: z.ZodType | undefined;
const externalRegistry = {
add(schema: z.ZodType): void {
externallyRegistered = schema;
},
};
expect(
publicInput.register(externalRegistry as never, undefined as never),
).toBe(publicInput);
expect(externallyRegistered).toBe(publicInput);
const partial = publicInput.partial();
const picked = publicInput.pick({ value: true });
expect(partial.safeParse({}).success).toBe(true);
expect(picked.safeParse({ value: "abcd" }).success).toBe(true);
expect((await publicInput.safeParseAsync({ value: "ab" })).success).toBe(
false,
);
expect((await publicInput.safeParseAsync({ value: "abcd" })).success).toBe(
true,
);
});
it("seals the global registry after deterministic bootstrap", () => {
sealHousekeepingCommandRegistry();