diff --git a/deno/lib/__tests__/string.test.ts b/deno/lib/__tests__/string.test.ts index bb967bafc..bd61c5bb5 100644 --- a/deno/lib/__tests__/string.test.ts +++ b/deno/lib/__tests__/string.test.ts @@ -297,6 +297,7 @@ test("url validations", () => { expect(() => url.parse("asdf")).toThrow(); expect(() => url.parse("https:/")).toThrow(); expect(() => url.parse("asdfj@lkjsdf.com")).toThrow(); + expect(() => url.parse("https://asdf.com,https://asdf")).toThrow(); }); test("url error overrides", () => { diff --git a/deno/lib/types.ts b/deno/lib/types.ts index cd09d4b15..d5043f644 100644 --- a/deno/lib/types.ts +++ b/deno/lib/types.ts @@ -640,6 +640,7 @@ const ulidRegex = /^[0-9A-HJKMNP-TV-Z]{26}$/i; // /^([a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[a-f0-9]{4}-[a-f0-9]{12}|00000000-0000-0000-0000-000000000000)$/i; const uuidRegex = /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/i; +const urlRegex = /^([a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*)(:\d+)?(\/[^\s,]*)?$/; const nanoidRegex = /^[a-z0-9_-]{21}$/i; const jwtRegex = /^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]*$/; const durationRegex = @@ -918,7 +919,10 @@ export class ZodString extends ZodType { } } else if (check.kind === "url") { try { - new URL(input.data); + const parsed = new URL(input.data); + if (!urlRegex.test(parsed.host + parsed.pathname)) { + throw new Error(); + } } catch { ctx = this._getOrReturnCtx(input, ctx); addIssueToContext(ctx, { diff --git a/src/__tests__/string.test.ts b/src/__tests__/string.test.ts index bc603a933..1ca5fd7e5 100644 --- a/src/__tests__/string.test.ts +++ b/src/__tests__/string.test.ts @@ -296,6 +296,7 @@ test("url validations", () => { expect(() => url.parse("asdf")).toThrow(); expect(() => url.parse("https:/")).toThrow(); expect(() => url.parse("asdfj@lkjsdf.com")).toThrow(); + expect(() => url.parse("https://asdf.com,https://asdf")).toThrow(); }); test("url error overrides", () => { diff --git a/src/types.ts b/src/types.ts index 98281ff2f..26e50eab8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -640,6 +640,7 @@ const ulidRegex = /^[0-9A-HJKMNP-TV-Z]{26}$/i; // /^([a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[a-f0-9]{4}-[a-f0-9]{12}|00000000-0000-0000-0000-000000000000)$/i; const uuidRegex = /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/i; +const urlRegex = /^([a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*)(:\d+)?(\/[^\s,]*)?$/; const nanoidRegex = /^[a-z0-9_-]{21}$/i; const jwtRegex = /^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]*$/; const durationRegex = @@ -918,7 +919,10 @@ export class ZodString extends ZodType { } } else if (check.kind === "url") { try { - new URL(input.data); + const parsed = new URL(input.data); + if (!urlRegex.test(parsed.host + parsed.pathname)) { + throw new Error(); + } } catch { ctx = this._getOrReturnCtx(input, ctx); addIssueToContext(ctx, {