zod
zod copied to clipboard
z.string().url() seems to accept any string
Hello, I think I may have found a bug with URL validation.
Indeed, I am trying to validate an object which has a property “ticketLink" that should be an URL (z.string().url())
This is my schema :
const schema = z.object({
projectId: z.string().uuid(),
title: z.string().min(5).max(100),
description: z.string().max(255).optional(),
punchTypeId: z.string().uuid(),
hours: z.number().min(0.25).max(24),
ticketLink: z.string().url(),
billable: z.boolean().optional(),
date: z.date(),
});
However, no matter what I am setting as a value for the ticketLink property, the schema does not return an error for this property, even if the value is not a valid URL.
For instance, validating this object :
const formState = {"date": 2023-03-23T20:00:05.466Z, "projectId": "28dfcf92-5b36-4976-1d48-08db1f3ca426", "ticketLink": "abc"}
schema.safeParse(formState)
doesn't raise an error for the ticketLink field, but "abc" should not be accepted as an URL.
We're using "zod": "3.21.4"
This is working fine for me, I'm getting the Invalid url message in the errors list.
import { z } from 'zod';
const schema = z.object({
projectId: z.string().uuid(),
title: z.string().min(5).max(100),
description: z.string().max(255).optional(),
punchTypeId: z.string().uuid(),
hours: z.number().min(0.25).max(24),
ticketLink: z.string().url(),
billable: z.boolean().optional(),
date: z.date(),
});
const formState = {
date: '2023-03-23T20:00:05.466Z',
projectId: '28dfcf92-5b36-4976-1d48-08db1f3ca426',
ticketLink: 'abc',
};
const result = schema.safeParse(formState);
if (!result.success) console.log(result.error.issues);
Everything is working as expected for me. Please send a reproducible example if you are still having issues.
const schema = z.object( {
ticketLink: z.string().url(),
} )
const formState = {
ticketLink: 'abc',
}
const result = schema.safeParse( formState )
!result.success && console.log( result.error.issues )
// [
// {
// validation: 'url',
// code: 'invalid_string',
// message: 'Invalid url',
// path: [ 'ticketLink' ]
// }
// ]
Thank you both for your response !
It is not impossible that there is a mistake in my code or something else I haven't setup properly...
I will retry and keep you updated.
I re-verified my code and can't understand why it still seems to skip the validation of this field.
Here's the real code snippet used in my app where I added some console.log for debugging. This function is a recoil selector in a react native app.
export const punchCreationFormValidationSelector = selector({
key: "punchCreationFormValidationSelector",
get: ({ get }) => {
const formState = get(punchCreationFormStateAtom);
const schema = z.object({
projectId: z.string({required_error: "Ce champ est requis"}).uuid("Identifiant du projet invalide"),
title: z.string({required_error: "Ce champ est requis"}).trim().min(5, "Le titre du punch doit avoir au moins 5 caractères").max(100, "Le titre du punch doit être inférieur à 100 caractères"),
description: z.string().max(255, "La description du punch doit être inférieure à 255 caractères").optional(),
punchTypeId: z.string({required_error: "Ce champ est requis"}).uuid("Identifiant du type de punch invalide"),
hours: z.coerce.number({required_error: "Ce champ est requis", invalid_type_error: "Ce champ est requis"}).min(0.25, "Le nombre d'heures doit être supérieur à 0.25").max(24, "Le nombre d'heures doit être inférieur à 24"),
ticketLink: z.string().url(),
billable: z.boolean(),
date: z.date({required_error: "Ce champ est requis"}),
})
console.log("Form state for validation", formState)
const result = schema.safeParse(formState);
console.log("result.success", result.success);
if (!result.success) {
const errors = result.error.flatten();
console.log(errors);
return {
success: false,
fieldErrors: errors.fieldErrors,
data: undefined,
}
} else {
return {
success: true,
fieldErrors: undefined,
data: result.data,
}
}
},
});
As we can see in the console output, the ticketLink field is defined with an invalid value in the formState object. Neverteless, result.success is true and there is no error for this field.
However, what's weird is that I tested essentially the same code in a standalone js file with the same version of Zod (outside the React app) and it works properly.
zodtest.mjs
import { z } from "zod";
const schema = z.object({
projectId: z.string({required_error: "Ce champ est requis"}).uuid("Identifiant du projet invalide"),
title: z.string({required_error: "Ce champ est requis"}).trim().min(5, "Le titre du punch doit avoir au moins 5 caractères").max(100, "Le titre du punch doit être inférieur à 100 caractères"),
description: z.string().max(255, "La description du punch doit être inférieure à 255 caractères").optional(),
punchTypeId: z.string({required_error: "Ce champ est requis"}).uuid("Identifiant du type de punch invalide"),
hours: z.coerce.number({required_error: "Ce champ est requis", invalid_type_error: "Ce champ est requis"}).min(0.25, "Le nombre d'heures doit être supérieur à 0.25").max(24, "Le nombre d'heures doit être inférieur à 24"),
ticketLink: z.string().url(),
billable: z.boolean(),
date: z.date({required_error: "Ce champ est requis"}),
});
const data = {
"billable": true,
"date": new Date("2023-03-20"),
"hours": "1",
"projectId": "28dfcf92-5b36-4976-1d48-08db1f3ca426",
"punchTypeId": "e1a28cff-b39e-4d5f-f0d6-08db2c16a4af",
"ticketLink": "notanurl",
"title": "Punch title"
}
console.log(data);
const result = schema.safeParse(data);
console.log(result.success);
console.log(result.error.flatten().fieldErrors);
So, I don't know if the issue is related to Zod or if it relates to recoil, but it's strange since all other fields are validated properly in both scenarios.
Another interesting thing I found is that if I add other validation rules to ticketLink e.g.
ticketLink: z.string().min(2).url() the min(2) rule is enforced properly.
For instance, ticketLink: 'ab' will not pass, but ticketLink: 'abc' does... which means that the formState.ticketLink field is indeed validated by zod, but the url() rule is not enforced.
Edit : using a regex() to verify the URL format also worked as expected, which lead me to think the real issue may be that url() do not work properly with React Native
@obrassard this is definitely a React Native new URL bug:
https://github.com/colinhacks/zod/issues/2256#issuecomment-1493780042
I couldn't find an upstream tracker issue - maybe that's something you could help us hunt down?:
https://github.com/facebook/react-native/issues/
Is there any update on this matter ? Or could you provide a specific way to handle this case on native with zod ? Thanks in advance.
Still facing this issue.
+1 can confirm it does not work in react native
+1 can confirm it does not work in react native also
I have the same problem in react native
I have the same issue in Vue 3
Are any corrections expected, or is it better to use some custom validation?
Using: "zod": "^3.22.4"
I can reproduce a similar issue in SvelteKit where the only thing required to return valid is that the string begins with a letter followed by a colon x:.
Returns Valid: http:example.com http:.......///broken.com http: a:example.com b: C: WWW:WWW.COM e:911 call:411
Returns Invalid: anything that does not start with a letter followed by a colon
Have reverted to using regex for urls for now.
Additional hostname validation solves most of edge cases but it prevents from using localhost and custom aliases.
Till then
const schema = z.object({
meetingLink: z
.string()
.refine((value) => /^(https?):\/\/(?=.*\.[a-z]{2,})[^\s$.?#].[^\s]*$/i.test(value), {
message: 'Please enter a valid URL',
}),
});
I am seeing this issue in Vue3, as well.
This URL is valid:
http://localenv.website.com/events/create
This URL (note the leading D) is not valid, but Zod passes validation:
Dhttp://localenv.website.com/events/create
Version:
"node_modules/zod": {
"version": "3.22.4",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz",
I am using this through @vee-validate/zod and this is the validation rule:
z.optional(z.string().url('Must be a valid URL'))
Till then
z.object({ meetingLink: z .string() .refine((value) => /^(https?):\/\//i.test(value), { message: 'Please enter a valid URL', }), });
Almost perfect... when I input "https://", it accepts :(
This worked for me: const urlRegex = /^(?:\w+:)?//([^\s.]+.\S{2}|localhost[:?\d])\S$/; const schema = z.object({ website: z.string().refine((value) => urlRegex.test(value), { message: 'Invalid URL format', }), });
Hiya, also having the same issue. :(
I'm having the same issue with react 18.2.0 (no react native) & zod 3.22.2
Till then
const schema = z.object({ meetingLink: z .string() .refine((value) => /^(https?):\/\/(?=.*\.[a-z]{2,})[^\s$.?#].[^\s]*$/i.test(value), { message: 'Please enter a valid URL', }), });
this works for me