Skip to content

fix: make "file://" fail url validation #1444

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

Merged
merged 1 commit into from
Jul 2, 2025

Conversation

bfabio
Copy link
Contributor

@bfabio bfabio commented Jun 27, 2025

"file://" is not a valid URL since it has no path.

Fixes Or Enhances

Makes "file://" fail the url validation instead of passing it.

This is akin to "http://" already failing as expected in tests.

Make sure that you've checked the boxes below before you submit PR:

  • Tests exist or have been written that cover this particular change.

@go-playground/validator-maintainers

@bfabio bfabio requested a review from a team as a code owner June 27, 2025 10:18
@coveralls
Copy link

coveralls commented Jun 27, 2025

Coverage Status

coverage: 73.677% (-0.009%) from 73.686%
when pulling 66619d5 on bfabio:url_file
into 3fd4678 on go-playground:master.

@deankarn
Copy link
Contributor

Thanks for the PR, will try and take a look this weekend.

I think there might be some other issue if file URI paths are passing the URL validation at all.

URL is a subset of URI which are specifically for resources hosted online, whereas file paths are local and are URI and not URLs.

@bfabio
Copy link
Contributor Author

bfabio commented Jun 27, 2025

Hi @deankarn, AFAIK they are also URLs: https://url.spec.whatwg.org/#scheme-relative-file-url-string

TIL that they can have a host as in file://localhost/etc/passwd and file:///etc/passwd is just /etc/passwd with an empty host.

baked_in.go Outdated
Comment on lines 1478 to 1482
if strings.HasPrefix(s, "file://") {
var u *url.URL
u, err = url.ParseRequestURI(s)

return err == nil && u.Path != ""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not let it fall through to url.Parse()? This should achieve the same no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's because for file:// we care only about u.Path, whereas the code below doesn't check for it and we'd end up hitting the last return (true)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ll have to look again because it should fail the host being blank and return false

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Host can be there even in file://, such as file://localhost/path/to/file.txt. We have a test case for that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

part of the file checks still fall through to the url parsing logic which feels strange the logic being split, WDYT about consolidating like this and only having url.Parse?

// isURL is the validation function for validating if the current field's value is a valid URL.
func isURL(fl FieldLevel) bool {
	field := fl.Field()

	//var err error
	switch field.Kind() {
	case reflect.String:

		s := strings.ToLower(field.String())

		if len(s) == 0 {
			return false
		}

		url, err := url.Parse(s)
		if err != nil || url.Scheme == "" {
			return false
		}

		isFileScheme := url.Scheme == "file"

		if (isFileScheme && (len(url.Path) == 0 || url.Path == "/")) || (!isFileScheme && len(url.Host) == 0 && len(url.Fragment) == 0 && len(url.Opaque) == 0) {
			return false
		}

		return true
	}

	panic(fmt.Sprintf("Bad field type %s", field.Type()))
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That looks cleaner. Why not url.Path == "" though (and, in general, using len() instead of comparing to the empty string)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

generally number equality checks should be faster that string equality is the only reason .. there may be some optimizations under the hood that make it close to the same but still number should be faster.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@deankarn TIL! Thanks

"file://" is not a valid URL since it has no path.

Co-authored-by: Dean Karn <github@deankarn.com>>>>
@deankarn deankarn merged commit cf2267f into go-playground:master Jul 2, 2025
10 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants