@@ -66,7 +66,7 @@ type testData struct {
6666 expectedContent string
6767}
6868
69- var testCases = []testData {
69+ var testCases = []testData { //nolint:gochecknoglobals
7070 {
7171 path : "foo/project/fileA.yaml" ,
7272 expectedContent : "fileA content" ,
@@ -88,7 +88,7 @@ var testCases = []testData{
8888func MakeFakeFs (td []testData ) filesys.FileSystem {
8989 fSys := filesys .MakeFsInMemory ()
9090 for _ , x := range td {
91- fSys .WriteFile (x .path , []byte (x .expectedContent ))
91+ _ = fSys .WriteFile (x .path , []byte (x .expectedContent ))
9292 }
9393 return fSys
9494}
@@ -161,31 +161,31 @@ func TestLoaderBadRelative(t *testing.T) {
161161 require .Equal ("/foo/project/subdir1" , l1 .Root ())
162162
163163 // Cannot cd into a file.
164- l2 , err : = l1 .New ("fileB.yaml" )
164+ _ , err = l1 .New ("fileB.yaml" )
165165 require .Error (err )
166166
167167 // It's not okay to stay at the same place.
168- l2 , err = l1 .New (filesys .SelfDir )
168+ _ , err = l1 .New (filesys .SelfDir )
169169 require .Error (err )
170170
171171 // It's not okay to go up and back down into same place.
172- l2 , err = l1 .New ("../subdir1" )
172+ _ , err = l1 .New ("../subdir1" )
173173 require .Error (err )
174174
175175 // It's not okay to go up via a relative path.
176- l2 , err = l1 .New (".." )
176+ _ , err = l1 .New (".." )
177177 require .Error (err )
178178
179179 // It's not okay to go up via an absolute path.
180- l2 , err = l1 .New ("/foo/project" )
180+ _ , err = l1 .New ("/foo/project" )
181181 require .Error (err )
182182
183183 // It's not okay to go to the root.
184- l2 , err = l1 .New ("/" )
184+ _ , err = l1 .New ("/" )
185185 require .Error (err )
186186
187187 // It's okay to go up and down to a sibling.
188- l2 , err = l1 .New ("../subdir2" )
188+ l2 , err : = l1 .New ("../subdir2" )
189189 require .NoError (err )
190190 require .Equal ("/foo/project/subdir2" , l2 .Root ())
191191
@@ -198,7 +198,7 @@ func TestLoaderBadRelative(t *testing.T) {
198198
199199 // It's not OK to go over to a previously visited directory.
200200 // Must disallow going back and forth in a cycle.
201- l1 , err = l2 .New ("../subdir1" )
201+ _ , err = l2 .New ("../subdir1" )
202202 require .Error (err )
203203}
204204
@@ -207,6 +207,60 @@ func TestNewEmptyLoader(t *testing.T) {
207207 require .Error (t , err )
208208}
209209
210+ func TestLoaderRejectsMalformedPath (t * testing.T ) {
211+ // A YAML indentation error can collapse two resource entries into one:
212+ // resources:
213+ // - ../../base
214+ // - ../../shared/prod
215+ // becomes the single string: "../../base - ../../shared/prod"
216+ //
217+ // filepath.Clean normalizes this to "../../shared/prod", silently
218+ // dropping the "../../base" reference. The loader must reject paths
219+ // with inner ".." components that cause this silent absorption.
220+ // See https://github.com/kubernetes-sigs/kustomize/issues/5979
221+ fSys := filesys .MakeFsInMemory ()
222+ require .NoError (t , fSys .MkdirAll ("/base" ))
223+ require .NoError (t , fSys .MkdirAll ("/shared/prod" ))
224+ require .NoError (t , fSys .MkdirAll ("/overlays/prod1" ))
225+
226+ l1 := NewLoaderOrDie (RestrictionNone , fSys , "/overlays/prod1" )
227+
228+ // The exact bug from issue #5979.
229+ _ , err := l1 .New ("../../base - ../../shared/prod" )
230+ require .Error (t , err )
231+ require .Contains (t , err .Error (), "ambiguous" )
232+ }
233+
234+ func TestHasAmbiguousPathSegment (t * testing.T ) {
235+ cases := map [string ]bool {
236+ // Safe: ".." only as standalone segments
237+ "../base" : false ,
238+ "../../shared/prod" : false ,
239+ ".." : false ,
240+ "foo/bar" : false ,
241+ "foo/bar/" : false ,
242+ "foo//bar" : false ,
243+ "./foo/bar" : false ,
244+ "https://root" : false ,
245+ "foo/../bar" : false ,
246+ "a/b/../../c" : false ,
247+ "a/.." : false ,
248+ // Winding paths are legitimate (used by localizer)
249+ "delta/../../../../a/b/../../alpha/beta/sibling" : false ,
250+ // Dangerous: segment contains embedded ".." from YAML merge errors
251+ "../../base - ../../shared/prod" : true ,
252+ "../../base ../../shared/prod" : true ,
253+ "foo/bar..baz/qux" : true ,
254+ "a/b/c..d/e" : true ,
255+ "../../base\t - ../../shared/prod" : true ,
256+ }
257+ for path , want := range cases {
258+ t .Run (path , func (t * testing.T ) {
259+ require .Equal (t , want , hasAmbiguousPathSegment (path ), "hasAmbiguousPathSegment(%q)" , path )
260+ })
261+ }
262+ }
263+
210264func TestNewRemoteLoaderDoesNotExist (t * testing.T ) {
211265 _ , err := makeLoader ().New ("https://example.com/org/repo" )
212266 require .ErrorContains (t , err , "fetch" )
@@ -265,23 +319,24 @@ const (
265319// └── exteriorData
266320func commonSetupForLoaderRestrictionTest (t * testing.T ) (string , filesys.FileSystem ) {
267321 t .Helper ()
322+ require := require .New (t )
268323 fSys , tmpDir := setupOnDisk (t )
269324 dir := tmpDir .String ()
270325
271- fSys .Mkdir (filepath .Join (dir , "base" ))
326+ require . NoError ( fSys .Mkdir (filepath .Join (dir , "base" ) ))
272327
273- fSys .WriteFile (
274- filepath .Join (dir , "base" , "okayData" ), []byte (contentOk ))
328+ require . NoError ( fSys .WriteFile (
329+ filepath .Join (dir , "base" , "okayData" ), []byte (contentOk )))
275330
276- fSys .WriteFile (
277- filepath .Join (dir , "exteriorData" ), []byte (contentExteriorData ))
331+ require . NoError ( fSys .WriteFile (
332+ filepath .Join (dir , "exteriorData" ), []byte (contentExteriorData )))
278333
279- os .Symlink (
334+ require . NoError ( os .Symlink (
280335 filepath .Join (dir , "base" , "okayData" ),
281- filepath .Join (dir , "base" , "symLinkToOkayData" ))
282- os .Symlink (
336+ filepath .Join (dir , "base" , "symLinkToOkayData" )))
337+ require . NoError ( os .Symlink (
283338 filepath .Join (dir , "exteriorData" ),
284- filepath .Join (dir , "base" , "symLinkToExteriorData" ))
339+ filepath .Join (dir , "base" , "symLinkToExteriorData" )))
285340 return dir , fSys
286341}
287342
@@ -391,14 +446,14 @@ func TestNewLoaderAtGitClone(t *testing.T) {
391446 url := rootURL + "/" + pathInRepo
392447 coRoot := "/tmp"
393448 fSys := filesys .MakeFsInMemory ()
394- fSys .MkdirAll (coRoot )
395- fSys .MkdirAll (coRoot + "/" + pathInRepo )
396- fSys .WriteFile (
449+ require . NoError ( fSys .MkdirAll (coRoot ) )
450+ require . NoError ( fSys .MkdirAll (coRoot + "/" + pathInRepo ) )
451+ require . NoError ( fSys .WriteFile (
397452 coRoot + "/" + pathInRepo + "/" +
398453 konfig .DefaultKustomizationFileName (),
399454 []byte (`
400455whatever
401- ` ))
456+ ` )))
402457
403458 repoSpec , err := git .NewRepoSpecFromURL (url )
404459 require .NoError (err )
@@ -418,7 +473,7 @@ whatever
418473 require .Error (err )
419474
420475 pathInRepo = "foo/overlay"
421- fSys .MkdirAll (coRoot + "/" + pathInRepo )
476+ require . NoError ( fSys .MkdirAll (coRoot + "/" + pathInRepo ) )
422477 url = rootURL + "/" + pathInRepo
423478 l2 , err := l .New (url )
424479 require .NoError (err )
@@ -435,9 +490,9 @@ func TestLoaderDisallowsLocalBaseFromRemoteOverlay(t *testing.T) {
435490 topDir := "/whatever"
436491 cloneRoot := topDir + "/someClone"
437492 fSys := filesys .MakeFsInMemory ()
438- fSys .MkdirAll (topDir + "/highBase" )
439- fSys .MkdirAll (cloneRoot + "/foo/base" )
440- fSys .MkdirAll (cloneRoot + "/foo/overlay" )
493+ require . NoError ( fSys .MkdirAll (topDir + "/highBase" ) )
494+ require . NoError ( fSys .MkdirAll (cloneRoot + "/foo/base" ) )
495+ require . NoError ( fSys .MkdirAll (cloneRoot + "/foo/overlay" ) )
441496
442497 var l1 ifc.Loader
443498
@@ -448,7 +503,7 @@ func TestLoaderDisallowsLocalBaseFromRemoteOverlay(t *testing.T) {
448503 require .Equal (cloneRoot + "/foo/overlay" , l1 .Root ())
449504
450505 l2 , err := l1 .New ("../base" )
451- require .NoError (nil )
506+ require .NoError (err )
452507 require .Equal (cloneRoot + "/foo/base" , l2 .Root ())
453508
454509 l3 , err := l2 .New ("../../../highBase" )
@@ -513,8 +568,8 @@ func TestLocalLoaderReferencingGitBase(t *testing.T) {
513568 topDir := "/whatever"
514569 cloneRoot := topDir + "/someClone"
515570 fSys := filesys .MakeFsInMemory ()
516- fSys .MkdirAll (topDir )
517- fSys .MkdirAll (cloneRoot + "/foo/base" )
571+ require . NoError ( fSys .MkdirAll (topDir ) )
572+ require . NoError ( fSys .MkdirAll (cloneRoot + "/foo/base" ) )
518573
519574 l1 := newLoaderAtConfirmedDir (
520575 RestrictionRootOnly , filesys .ConfirmedDir (topDir ), fSys , nil ,
@@ -534,8 +589,8 @@ func TestRepoDirectCycleDetection(t *testing.T) {
534589 topDir := "/cycles"
535590 cloneRoot := topDir + "/someClone"
536591 fSys := filesys .MakeFsInMemory ()
537- fSys .MkdirAll (topDir )
538- fSys .MkdirAll (cloneRoot )
592+ require . NoError ( fSys .MkdirAll (topDir ) )
593+ require . NoError ( fSys .MkdirAll (cloneRoot ) )
539594
540595 l1 := newLoaderAtConfirmedDir (
541596 RestrictionRootOnly , filesys .ConfirmedDir (topDir ), fSys , nil ,
@@ -556,8 +611,8 @@ func TestRepoIndirectCycleDetection(t *testing.T) {
556611 topDir := "/cycles"
557612 cloneRoot := topDir + "/someClone"
558613 fSys := filesys .MakeFsInMemory ()
559- fSys .MkdirAll (topDir )
560- fSys .MkdirAll (cloneRoot )
614+ require . NoError ( fSys .MkdirAll (topDir ) )
615+ require . NoError ( fSys .MkdirAll (cloneRoot ) )
561616
562617 l0 := newLoaderAtConfirmedDir (
563618 RestrictionRootOnly , filesys .ConfirmedDir (topDir ), fSys , nil ,
@@ -629,7 +684,7 @@ func TestLoaderHTTP(t *testing.T) {
629684 u := req .URL .String ()
630685 require .Equal (x .path , u )
631686 return & http.Response {
632- StatusCode : 200 ,
687+ StatusCode : http . StatusOK ,
633688 Body : io .NopCloser (bytes .NewBufferString (x .expectedContent )),
634689 Header : make (http.Header ),
635690 }
0 commit comments