fix(validation): stop unrecognised boolean strings coercing to a silent false
checkField in packages/validation/src/index.ts (and its browser mirror in runtime.ts) treated any string other than "true"/"on" as false with no error, so typos like "treu" or values like "yes"/"1"/"TRUE" silently passed as false. Now: - true/false booleans pass through unchanged - recognised true strings (case-insensitive, trimmed): true, on, 1, yes - recognised false strings: false, off, 0, no - numeric 1/0 coerce (JSON payloads) - undefined/null/"" still coerce to false (unchecked-checkbox semantics) - anything else is now a type error (desc.typeMessage or "Must be true or false") instead of a silent false Locked-in behaviours preserved: a required boolean given false still errors, and parseEnv DEBUG: "true" coercion still works. Added coverage for recognised strings, numeric 1/0, the type-error regression guard, absent/empty handling, the required+false case, and a client/server parity test driving both checkField and the browser runtime through the same inputs. Blast radius: searched packages/, examples/, services/ for v.boolean() usage; all existing call sites (auth consent/rememberDevice, db 'active' default, example consent checkboxes) feed true/false/'on'/absent values, none of which change behavior. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3ae5d7cf97
commit
281615a4b0
4 files changed
+193
-2
No files matched your search
@@ -114,7 +114,28 @@ export function checkField(
|
||||
}
|
||||
|
||||
if (desc.type === "boolean") {
|
||||
const value = raw === true || raw === "true" || raw === "on";
|
||||
const empty = raw === undefined || raw === null || raw === "";
|
||||
let value: boolean;
|
||||
if (typeof raw === "boolean") {
|
||||
value = raw;
|
||||
} else if (empty) {
|
||||
value = false;
|
||||
} else if (raw === 1) {
|
||||
value = true;
|
||||
} else if (raw === 0) {
|
||||
value = false;
|
||||
} else if (typeof raw === "string") {
|
||||
const norm = raw.trim().toLowerCase();
|
||||
if (norm === "true" || norm === "on" || norm === "1" || norm === "yes") {
|
||||
value = true;
|
||||
} else if (norm === "false" || norm === "off" || norm === "0" || norm === "no") {
|
||||
value = false;
|
||||
} else {
|
||||
return { value: raw, error: desc.typeMessage ?? "Must be true or false" };
|
||||
}
|
||||
} else {
|
||||
return { value: raw, error: desc.typeMessage ?? "Must be true or false" };
|
||||
}
|
||||
if (!desc.optional && !value) {
|
||||
return { value, error: desc.requiredMessage || "Required" };
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user