github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/pkg/archive/diff_test.go (about) 1 package archive // import "github.com/demonoid81/moby/pkg/archive" 2 3 import ( 4 "archive/tar" 5 "io" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "reflect" 10 "testing" 11 12 "github.com/demonoid81/moby/pkg/ioutils" 13 ) 14 15 func TestApplyLayerInvalidFilenames(t *testing.T) { 16 for i, headers := range [][]*tar.Header{ 17 { 18 { 19 Name: "../victim/dotdot", 20 Typeflag: tar.TypeReg, 21 Mode: 0644, 22 }, 23 }, 24 { 25 { 26 // Note the leading slash 27 Name: "/../victim/slash-dotdot", 28 Typeflag: tar.TypeReg, 29 Mode: 0644, 30 }, 31 }, 32 } { 33 if err := testBreakout("applylayer", "docker-TestApplyLayerInvalidFilenames", headers); err != nil { 34 t.Fatalf("i=%d. %v", i, err) 35 } 36 } 37 } 38 39 func TestApplyLayerInvalidHardlink(t *testing.T) { 40 for i, headers := range [][]*tar.Header{ 41 { // try reading victim/hello (../) 42 { 43 Name: "dotdot", 44 Typeflag: tar.TypeLink, 45 Linkname: "../victim/hello", 46 Mode: 0644, 47 }, 48 }, 49 { // try reading victim/hello (/../) 50 { 51 Name: "slash-dotdot", 52 Typeflag: tar.TypeLink, 53 // Note the leading slash 54 Linkname: "/../victim/hello", 55 Mode: 0644, 56 }, 57 }, 58 { // try writing victim/file 59 { 60 Name: "loophole-victim", 61 Typeflag: tar.TypeLink, 62 Linkname: "../victim", 63 Mode: 0755, 64 }, 65 { 66 Name: "loophole-victim/file", 67 Typeflag: tar.TypeReg, 68 Mode: 0644, 69 }, 70 }, 71 { // try reading victim/hello (hardlink, symlink) 72 { 73 Name: "loophole-victim", 74 Typeflag: tar.TypeLink, 75 Linkname: "../victim", 76 Mode: 0755, 77 }, 78 { 79 Name: "symlink", 80 Typeflag: tar.TypeSymlink, 81 Linkname: "loophole-victim/hello", 82 Mode: 0644, 83 }, 84 }, 85 { // Try reading victim/hello (hardlink, hardlink) 86 { 87 Name: "loophole-victim", 88 Typeflag: tar.TypeLink, 89 Linkname: "../victim", 90 Mode: 0755, 91 }, 92 { 93 Name: "hardlink", 94 Typeflag: tar.TypeLink, 95 Linkname: "loophole-victim/hello", 96 Mode: 0644, 97 }, 98 }, 99 { // Try removing victim directory (hardlink) 100 { 101 Name: "loophole-victim", 102 Typeflag: tar.TypeLink, 103 Linkname: "../victim", 104 Mode: 0755, 105 }, 106 { 107 Name: "loophole-victim", 108 Typeflag: tar.TypeReg, 109 Mode: 0644, 110 }, 111 }, 112 } { 113 if err := testBreakout("applylayer", "docker-TestApplyLayerInvalidHardlink", headers); err != nil { 114 t.Fatalf("i=%d. %v", i, err) 115 } 116 } 117 } 118 119 func TestApplyLayerInvalidSymlink(t *testing.T) { 120 for i, headers := range [][]*tar.Header{ 121 { // try reading victim/hello (../) 122 { 123 Name: "dotdot", 124 Typeflag: tar.TypeSymlink, 125 Linkname: "../victim/hello", 126 Mode: 0644, 127 }, 128 }, 129 { // try reading victim/hello (/../) 130 { 131 Name: "slash-dotdot", 132 Typeflag: tar.TypeSymlink, 133 // Note the leading slash 134 Linkname: "/../victim/hello", 135 Mode: 0644, 136 }, 137 }, 138 { // try writing victim/file 139 { 140 Name: "loophole-victim", 141 Typeflag: tar.TypeSymlink, 142 Linkname: "../victim", 143 Mode: 0755, 144 }, 145 { 146 Name: "loophole-victim/file", 147 Typeflag: tar.TypeReg, 148 Mode: 0644, 149 }, 150 }, 151 { // try reading victim/hello (symlink, symlink) 152 { 153 Name: "loophole-victim", 154 Typeflag: tar.TypeSymlink, 155 Linkname: "../victim", 156 Mode: 0755, 157 }, 158 { 159 Name: "symlink", 160 Typeflag: tar.TypeSymlink, 161 Linkname: "loophole-victim/hello", 162 Mode: 0644, 163 }, 164 }, 165 { // try reading victim/hello (symlink, hardlink) 166 { 167 Name: "loophole-victim", 168 Typeflag: tar.TypeSymlink, 169 Linkname: "../victim", 170 Mode: 0755, 171 }, 172 { 173 Name: "hardlink", 174 Typeflag: tar.TypeLink, 175 Linkname: "loophole-victim/hello", 176 Mode: 0644, 177 }, 178 }, 179 { // try removing victim directory (symlink) 180 { 181 Name: "loophole-victim", 182 Typeflag: tar.TypeSymlink, 183 Linkname: "../victim", 184 Mode: 0755, 185 }, 186 { 187 Name: "loophole-victim", 188 Typeflag: tar.TypeReg, 189 Mode: 0644, 190 }, 191 }, 192 } { 193 if err := testBreakout("applylayer", "docker-TestApplyLayerInvalidSymlink", headers); err != nil { 194 t.Fatalf("i=%d. %v", i, err) 195 } 196 } 197 } 198 199 func TestApplyLayerWhiteouts(t *testing.T) { 200 wd, err := ioutil.TempDir("", "graphdriver-test-whiteouts") 201 if err != nil { 202 return 203 } 204 defer os.RemoveAll(wd) 205 206 base := []string{ 207 ".baz", 208 "bar/", 209 "bar/bax", 210 "bar/bay/", 211 "baz", 212 "foo/", 213 "foo/.abc", 214 "foo/.bcd/", 215 "foo/.bcd/a", 216 "foo/cde/", 217 "foo/cde/def", 218 "foo/cde/efg", 219 "foo/fgh", 220 "foobar", 221 } 222 223 type tcase struct { 224 change, expected []string 225 } 226 227 tcases := []tcase{ 228 { 229 base, 230 base, 231 }, 232 { 233 []string{ 234 ".bay", 235 ".wh.baz", 236 "foo/", 237 "foo/.bce", 238 "foo/.wh..wh..opq", 239 "foo/cde/", 240 "foo/cde/efg", 241 }, 242 []string{ 243 ".bay", 244 ".baz", 245 "bar/", 246 "bar/bax", 247 "bar/bay/", 248 "foo/", 249 "foo/.bce", 250 "foo/cde/", 251 "foo/cde/efg", 252 "foobar", 253 }, 254 }, 255 { 256 []string{ 257 ".bay", 258 ".wh..baz", 259 ".wh.foobar", 260 "foo/", 261 "foo/.abc", 262 "foo/.wh.cde", 263 "bar/", 264 }, 265 []string{ 266 ".bay", 267 "bar/", 268 "bar/bax", 269 "bar/bay/", 270 "foo/", 271 "foo/.abc", 272 "foo/.bce", 273 }, 274 }, 275 { 276 []string{ 277 ".abc", 278 ".wh..wh..opq", 279 "foobar", 280 }, 281 []string{ 282 ".abc", 283 "foobar", 284 }, 285 }, 286 } 287 288 for i, tc := range tcases { 289 l, err := makeTestLayer(tc.change) 290 if err != nil { 291 t.Fatal(err) 292 } 293 294 _, err = UnpackLayer(wd, l, nil) 295 if err != nil { 296 t.Fatal(err) 297 } 298 err = l.Close() 299 if err != nil { 300 t.Fatal(err) 301 } 302 303 paths, err := readDirContents(wd) 304 if err != nil { 305 t.Fatal(err) 306 } 307 308 if !reflect.DeepEqual(tc.expected, paths) { 309 t.Fatalf("invalid files for layer %d: expected %q, got %q", i, tc.expected, paths) 310 } 311 } 312 313 } 314 315 func makeTestLayer(paths []string) (rc io.ReadCloser, err error) { 316 tmpDir, err := ioutil.TempDir("", "graphdriver-test-mklayer") 317 if err != nil { 318 return 319 } 320 defer func() { 321 if err != nil { 322 os.RemoveAll(tmpDir) 323 } 324 }() 325 for _, p := range paths { 326 // Source files are always in Unix format. But we use filepath on 327 // creation to be platform agnostic. 328 if p[len(p)-1] == '/' { 329 if err = os.MkdirAll(filepath.Join(tmpDir, p), 0700); err != nil { 330 return 331 } 332 } else { 333 if err = ioutil.WriteFile(filepath.Join(tmpDir, p), nil, 0600); err != nil { 334 return 335 } 336 } 337 } 338 archive, err := Tar(tmpDir, Uncompressed) 339 if err != nil { 340 return 341 } 342 return ioutils.NewReadCloserWrapper(archive, func() error { 343 err := archive.Close() 344 os.RemoveAll(tmpDir) 345 return err 346 }), nil 347 } 348 349 func readDirContents(root string) ([]string, error) { 350 var files []string 351 err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { 352 if err != nil { 353 return err 354 } 355 if path == root { 356 return nil 357 } 358 rel, err := filepath.Rel(root, path) 359 if err != nil { 360 return err 361 } 362 if info.IsDir() { 363 rel = rel + string(filepath.Separator) 364 } 365 // Append in Unix semantics 366 files = append(files, filepath.ToSlash(rel)) 367 return nil 368 }) 369 if err != nil { 370 return nil, err 371 } 372 return files, nil 373 }