diff options
| -rw-r--r-- | internal/vuln/source_test.go | 4 | ||||
| -rw-r--r-- | internal/vuln/url.go | 13 | ||||
| -rw-r--r-- | internal/vuln/url_other.go | 21 | ||||
| -rw-r--r-- | internal/vuln/url_test.go | 2 | ||||
| -rw-r--r-- | internal/vuln/url_windows.go | 43 |
5 files changed, 65 insertions, 18 deletions
diff --git a/internal/vuln/source_test.go b/internal/vuln/source_test.go index 6eff01c8..03893e41 100644 --- a/internal/vuln/source_test.go +++ b/internal/vuln/source_test.go @@ -12,16 +12,12 @@ import ( "net/http/httptest" "os" "path/filepath" - "runtime" "testing" "golang.org/x/pkgsite/internal/osv" ) func TestNewSource(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("windows is not supported (see convertFileURLPath") - } t.Run("https", func(t *testing.T) { url := "https://vuln.go.dev" s, err := NewSource(url) diff --git a/internal/vuln/url.go b/internal/vuln/url.go index 60e2d1ea..67774c13 100644 --- a/internal/vuln/url.go +++ b/internal/vuln/url.go @@ -12,7 +12,6 @@ import ( "errors" "net/url" "path/filepath" - "runtime" ) var errNotAbsolute = errors.New("path is not absolute") @@ -43,15 +42,3 @@ func URLToFilePath(u *url.URL) (string, error) { } return checkAbs(path) } - -func convertFileURLPath(host, path string) (string, error) { - if runtime.GOOS == "windows" { - return "", errors.New("windows not supported") - } - switch host { - case "", "localhost": - default: - return "", errors.New("file URL specifies non-local host") - } - return filepath.FromSlash(path), nil -} diff --git a/internal/vuln/url_other.go b/internal/vuln/url_other.go new file mode 100644 index 00000000..6694390b --- /dev/null +++ b/internal/vuln/url_other.go @@ -0,0 +1,21 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !windows + +package vuln + +import ( + "errors" + "path/filepath" +) + +func convertFileURLPath(host, path string) (string, error) { + switch host { + case "", "localhost": + default: + return "", errors.New("file URL specifies non-local host") + } + return filepath.FromSlash(path), nil +} diff --git a/internal/vuln/url_test.go b/internal/vuln/url_test.go index 22334cb7..46908568 100644 --- a/internal/vuln/url_test.go +++ b/internal/vuln/url_test.go @@ -12,7 +12,7 @@ import ( func TestURLToFilePath(t *testing.T) { if runtime.GOOS == "windows" { - t.Skip("windows is not supported (see convertFileURLPath") + t.Skip("windows is not supported (see convertFileURLPath)") } for _, tc := range urlTests { if tc.url == "" { diff --git a/internal/vuln/url_windows.go b/internal/vuln/url_windows.go new file mode 100644 index 00000000..bb649697 --- /dev/null +++ b/internal/vuln/url_windows.go @@ -0,0 +1,43 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package vuln + +import ( + "errors" + "path/filepath" + "strings" +) + +func convertFileURLPath(host, path string) (string, error) { + if len(path) == 0 || path[0] != '/' { + return "", errNotAbsolute + } + + path = filepath.FromSlash(path) + + // We interpret Windows file URLs per the description in + // https://blogs.msdn.microsoft.com/ie/2006/12/06/file-uris-in-windows/. + + // The host part of a file URL (if any) is the UNC volume name, + // but RFC 8089 reserves the authority "localhost" for the local machine. + if host != "" && host != "localhost" { + // A common "legacy" format omits the leading slash before a drive letter, + // encoding the drive letter as the host instead of part of the path. + // (See https://blogs.msdn.microsoft.com/freeassociations/2005/05/19/the-bizarre-and-unhappy-story-of-file-urls/.) + // We do not support that format, but we should at least emit a more + // helpful error message for it. + if filepath.VolumeName(host) != "" { + return "", errors.New("file URL encodes drive letter in host field (example file:///C:/path)") + } + return `\\` + host + path, nil + } + + // If host is empty, path must contain an initial slash followed by a + // drive letter and path. Remove the slash and verify that the path is valid. + if vol := filepath.VolumeName(path[1:]); vol == "" || strings.HasPrefix(vol, `\\`) { + return "", errors.New("file URL missing drive letter (e.g., file:///C:/path)") + } + return filepath.Clean(path[1:]), nil +} |
