1- // Copyright (C) 2017-2024 SUSE LLC. All rights reserved.
1+ // Copyright (C) 2017-2025 SUSE LLC. All rights reserved.
22// Use of this source code is governed by a BSD-style
33// license that can be found in the LICENSE file.
44
@@ -12,6 +12,8 @@ import (
1212 "runtime"
1313 "syscall"
1414 "testing"
15+
16+ "github.com/stretchr/testify/assert"
1517)
1618
1719// TODO: These tests won't work on plan9 because it doesn't have symlinks, and
@@ -392,8 +394,59 @@ func TestSecureJoinVFSErrors(t *testing.T) {
392394}
393395
394396func TestUncleanRoot (t * testing.T ) {
395- safePath , err := SecureJoin ("/foo/.." , "bar/baz" )
396- if ! errors .Is (err , errUncleanRoot ) {
397- t .Errorf ("SecureJoin with non-clean path should return errUncleanRoot, instead got (%q, %v)" , safePath , err )
397+ root := t .TempDir ()
398+
399+ for _ , test := range []struct {
400+ testName , root string
401+ expectedErr error
402+ }{
403+ {"trailing-dotdot" , "foo/.." , errUnsafeRoot },
404+ {"leading-dotdot" , "../foo" , errUnsafeRoot },
405+ {"middle-dotdot" , "../foo" , errUnsafeRoot },
406+ {"many-dotdot" , "foo/../foo/../a" , errUnsafeRoot },
407+ {"trailing-slash" , root + "/foo/bar/" , nil },
408+ {"trailing-slashes" , root + "/foo/bar///" , nil },
409+ {"many-slashes" , root + "/foo///bar////baz" , nil },
410+ {"plain-dot" , root + "/foo/./bar" , nil },
411+ {"many-dot" , root + "/foo/./bar/./." , nil },
412+ {"unclean-safe" , root + "/foo///./bar/.///.///" , nil },
413+ {"unclean-unsafe" , root + "/foo///./bar/..///.///" , errUnsafeRoot },
414+ } {
415+ test := test // copy iterator
416+ t .Run (test .testName , func (t * testing.T ) {
417+ _ , err := SecureJoin (test .root , "foo/bar/baz" )
418+ if test .expectedErr != nil {
419+ assert .ErrorIsf (t , err , test .expectedErr , "SecureJoin with unsafe root %q" , test .root )
420+ } else {
421+ assert .NoErrorf (t , err , "SecureJoin with safe but unclean root %q" , test .root )
422+ }
423+ })
424+ }
425+ }
426+
427+ func TestHasDotDot (t * testing.T ) {
428+ for _ , test := range []struct {
429+ testName , path string
430+ expected bool
431+ }{
432+ {"plain-dotdot" , ".." , true },
433+ {"trailing-dotdot" , "foo/bar/baz/.." , true },
434+ {"leading-dotdot" , "../foo/bar/baz" , true },
435+ {"middle-dotdot" , "foo/bar/../baz" , true },
436+ {"dotdot-in-name1" , "foo/..bar/baz" , false },
437+ {"dotdot-in-name2" , "foo/bar../baz" , false },
438+ {"dotdot-in-name3" , "foo/b..r/baz" , false },
439+ {"dotdot-in-name4" , "..foo/bar/baz" , false },
440+ {"dotdot-in-name5" , "foo/bar/baz.." , false },
441+ {"dot1" , "./foo/bar/baz" , false },
442+ {"dot2" , "foo/bar/baz/." , false },
443+ {"dot3" , "foo/././bar/baz" , false },
444+ {"unclean" , "foo//.//bar/baz////" , false },
445+ } {
446+ test := test // copy iterator
447+ t .Run (test .testName , func (t * testing.T ) {
448+ got := hasDotDot (test .path )
449+ assert .Equalf (t , test .expected , got , "unexpected result for hasDotDot(%q)" , test .path )
450+ })
398451 }
399452}
0 commit comments