1
0
mirror of https://github.com/golang/go synced 2024-09-30 00:04:28 -06:00

path/filepath: don't drop .. elements when cleaning invalid Windows paths

Fix a bug where Clean could improperly drop .. elements from a
path on Windows, when the path contains elements containing a ':'.

For example, Clean("a/../b:/../../c") now correctly returns "..\c"
rather than "c".

Fixes #61866

Change-Id: I97b0238953c183b2ce19ca89c14f26700008ea72
Reviewed-on: https://go-review.googlesource.com/c/go/+/517216
Run-TryBot: Damien Neil <dneil@google.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Quim Muntal <quimmuntal@gmail.com>
This commit is contained in:
Damien Neil 2023-08-08 14:07:08 -07:00
parent fe1daf2e43
commit 6e43407931
2 changed files with 25 additions and 12 deletions

View File

@ -16,6 +16,7 @@ import (
"io/fs"
"os"
"runtime"
"slices"
"sort"
"strings"
)
@ -52,6 +53,11 @@ func (b *lazybuf) append(c byte) {
b.w++
}
func (b *lazybuf) prepend(prefix ...byte) {
b.buf = slices.Insert(b.buf, 0, prefix...)
b.w += len(prefix)
}
func (b *lazybuf) string() string {
if b.buf == nil {
return b.volAndPath[:b.volLen+b.w]
@ -150,18 +156,6 @@ func Clean(path string) string {
if rooted && out.w != 1 || !rooted && out.w != 0 {
out.append(Separator)
}
// If a ':' appears in the path element at the start of a Windows path,
// insert a .\ at the beginning to avoid converting relative paths
// like a/../c: into c:.
if runtime.GOOS == "windows" && out.w == 0 && out.volLen == 0 && r != 0 {
for i := r; i < n && !os.IsPathSeparator(path[i]); i++ {
if path[i] == ':' {
out.append('.')
out.append(Separator)
break
}
}
}
// copy element
for ; r < n && !os.IsPathSeparator(path[r]); r++ {
out.append(path[r])
@ -174,6 +168,21 @@ func Clean(path string) string {
out.append('.')
}
if runtime.GOOS == "windows" && out.volLen == 0 && out.buf != nil {
// If a ':' appears in the path element at the start of a Windows path,
// insert a .\ at the beginning to avoid converting relative paths
// like a/../c: into c:.
for _, c := range out.buf {
if os.IsPathSeparator(c) {
break
}
if c == ':' {
out.prepend('.', Separator)
break
}
}
}
return FromSlash(out.string())
}

View File

@ -67,6 +67,7 @@ var cleantests = []PathTest{
{"/abc/def/../../..", "/"},
{"abc/def/../../../ghi/jkl/../../../mno", "../../mno"},
{"/../abc", "/abc"},
{"a/../b:/../../c", `../c`},
// Combinations
{"abc/./../def", "def"},
@ -89,6 +90,7 @@ var wincleantests = []PathTest{
{`c:\abc\def\..\..`, `c:\`},
{`c:\..\abc`, `c:\abc`},
{`c:..\abc`, `c:..\abc`},
{`c:\b:\..\..\..\d`, `c:\d`},
{`\`, `\`},
{`/`, `\`},
{`\\i\..\c$`, `\\i\..\c$`},
@ -169,6 +171,7 @@ var islocaltests = []IsLocalTest{
{"a/", true},
{"a/.", true},
{"a/./b/./c", true},
{`a/../b:/../../c`, false},
}
var winislocaltests = []IsLocalTest{
@ -380,6 +383,7 @@ var winjointests = []JoinTest{
{[]string{`\\a`, `b`, `c`}, `\\a\b\c`},
{[]string{`\\a\`, `b`, `c`}, `\\a\b\c`},
{[]string{`//`, `a`}, `\\a`},
{[]string{`a:\b\c`, `x\..\y:\..\..\z`}, `a:\b\z`},
}
func TestJoin(t *testing.T) {