Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

z.string().url() seems to accept any string #2236

Open
obrassard opened this issue Mar 23, 2023 · 22 comments · Fixed by #3049
Open

z.string().url() seems to accept any string #2236

obrassard opened this issue Mar 23, 2023 · 22 comments · Fixed by #3049
Labels
bug-unconfirmed Bug report that isn't confirmed

Comments

@obrassard
Copy link

obrassard commented Mar 23, 2023

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"

@ahmafi
Copy link

ahmafi commented Mar 25, 2023

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);

@JacobWeisenburger JacobWeisenburger added the bug-unconfirmed Bug report that isn't confirmed label Mar 27, 2023
@JacobWeisenburger
Copy link
Contributor

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' ]
//     }
// ]

@obrassard
Copy link
Author

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.

@obrassard
Copy link
Author

obrassard commented Mar 28, 2023

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.

Capture d’écran, le 2023-03-28 à 16 54 19

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);

Capture d’écran, le 2023-03-28 à 17 01 25

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.

@obrassard
Copy link
Author

obrassard commented Mar 28, 2023

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

@crutchcorn
Copy link
Contributor

crutchcorn commented Apr 3, 2023

@obrassard this is definitely a React Native new URL bug:

#2256 (comment)

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/

@anckaertv
Copy link

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.

@delivey
Copy link

delivey commented Jun 15, 2023

Still facing this issue.

@1finedev
Copy link

+1 can confirm it does not work in react native

@Tonysw2
Copy link

Tonysw2 commented Sep 12, 2023

+1 can confirm it does not work in react native also

@axinzi
Copy link

axinzi commented Sep 27, 2023

I have the same problem in react native

@krisztina-tropee
Copy link

I have the same issue in Vue 3

@aaalll
Copy link

aaalll commented Nov 10, 2023

Are any corrections expected, or is it better to use some custom validation?

@ThingEngineer
Copy link

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.

@RobertPechaCZ
Copy link

Additional hostname validation solves most of edge cases but it prevents from using localhost and custom aliases.

@badalsaibo
Copy link

badalsaibo commented Jan 31, 2024

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',
    }),
});

@ekeyte
Copy link

ekeyte commented Mar 18, 2024

I am seeing this issue in Vue3, as well.

This URL is valid:
https://localenv.website.com/events/create

This URL (note the leading D) is not valid, but Zod passes validation:
Dhttps://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'))

@c0h1b4
Copy link

c0h1b4 commented Mar 29, 2024

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 :(

@jarriola-designli
Copy link

jarriola-designli commented May 9, 2024

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',
}),
});

@vexkiddy
Copy link

Hiya, also having the same issue. :(

@daviddwmd
Copy link

I'm having the same issue with react 18.2.0 (no react native) & zod 3.22.2

@IvanVeljkovic
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug-unconfirmed Bug report that isn't confirmed
Projects
None yet
Development

Successfully merging a pull request may close this issue.