123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371 |
- package archive // import "github.com/docker/docker/pkg/archive"
- import (
- "archive/tar"
- "io"
- "os"
- "path/filepath"
- "reflect"
- "testing"
- "github.com/docker/docker/pkg/ioutils"
- )
- func TestApplyLayerInvalidFilenames(t *testing.T) {
- for i, headers := range [][]*tar.Header{
- {
- {
- Name: "../victim/dotdot",
- Typeflag: tar.TypeReg,
- Mode: 0o644,
- },
- },
- {
- {
- // Note the leading slash
- Name: "/../victim/slash-dotdot",
- Typeflag: tar.TypeReg,
- Mode: 0o644,
- },
- },
- } {
- if err := testBreakout("applylayer", "docker-TestApplyLayerInvalidFilenames", headers); err != nil {
- t.Fatalf("i=%d. %v", i, err)
- }
- }
- }
- func TestApplyLayerInvalidHardlink(t *testing.T) {
- for i, headers := range [][]*tar.Header{
- { // try reading victim/hello (../)
- {
- Name: "dotdot",
- Typeflag: tar.TypeLink,
- Linkname: "../victim/hello",
- Mode: 0o644,
- },
- },
- { // try reading victim/hello (/../)
- {
- Name: "slash-dotdot",
- Typeflag: tar.TypeLink,
- // Note the leading slash
- Linkname: "/../victim/hello",
- Mode: 0o644,
- },
- },
- { // try writing victim/file
- {
- Name: "loophole-victim",
- Typeflag: tar.TypeLink,
- Linkname: "../victim",
- Mode: 0o755,
- },
- {
- Name: "loophole-victim/file",
- Typeflag: tar.TypeReg,
- Mode: 0o644,
- },
- },
- { // try reading victim/hello (hardlink, symlink)
- {
- Name: "loophole-victim",
- Typeflag: tar.TypeLink,
- Linkname: "../victim",
- Mode: 0o755,
- },
- {
- Name: "symlink",
- Typeflag: tar.TypeSymlink,
- Linkname: "loophole-victim/hello",
- Mode: 0o644,
- },
- },
- { // Try reading victim/hello (hardlink, hardlink)
- {
- Name: "loophole-victim",
- Typeflag: tar.TypeLink,
- Linkname: "../victim",
- Mode: 0o755,
- },
- {
- Name: "hardlink",
- Typeflag: tar.TypeLink,
- Linkname: "loophole-victim/hello",
- Mode: 0o644,
- },
- },
- { // Try removing victim directory (hardlink)
- {
- Name: "loophole-victim",
- Typeflag: tar.TypeLink,
- Linkname: "../victim",
- Mode: 0o755,
- },
- {
- Name: "loophole-victim",
- Typeflag: tar.TypeReg,
- Mode: 0o644,
- },
- },
- } {
- if err := testBreakout("applylayer", "docker-TestApplyLayerInvalidHardlink", headers); err != nil {
- t.Fatalf("i=%d. %v", i, err)
- }
- }
- }
- func TestApplyLayerInvalidSymlink(t *testing.T) {
- for i, headers := range [][]*tar.Header{
- { // try reading victim/hello (../)
- {
- Name: "dotdot",
- Typeflag: tar.TypeSymlink,
- Linkname: "../victim/hello",
- Mode: 0o644,
- },
- },
- { // try reading victim/hello (/../)
- {
- Name: "slash-dotdot",
- Typeflag: tar.TypeSymlink,
- // Note the leading slash
- Linkname: "/../victim/hello",
- Mode: 0o644,
- },
- },
- { // try writing victim/file
- {
- Name: "loophole-victim",
- Typeflag: tar.TypeSymlink,
- Linkname: "../victim",
- Mode: 0o755,
- },
- {
- Name: "loophole-victim/file",
- Typeflag: tar.TypeReg,
- Mode: 0o644,
- },
- },
- { // try reading victim/hello (symlink, symlink)
- {
- Name: "loophole-victim",
- Typeflag: tar.TypeSymlink,
- Linkname: "../victim",
- Mode: 0o755,
- },
- {
- Name: "symlink",
- Typeflag: tar.TypeSymlink,
- Linkname: "loophole-victim/hello",
- Mode: 0o644,
- },
- },
- { // try reading victim/hello (symlink, hardlink)
- {
- Name: "loophole-victim",
- Typeflag: tar.TypeSymlink,
- Linkname: "../victim",
- Mode: 0o755,
- },
- {
- Name: "hardlink",
- Typeflag: tar.TypeLink,
- Linkname: "loophole-victim/hello",
- Mode: 0o644,
- },
- },
- { // try removing victim directory (symlink)
- {
- Name: "loophole-victim",
- Typeflag: tar.TypeSymlink,
- Linkname: "../victim",
- Mode: 0o755,
- },
- {
- Name: "loophole-victim",
- Typeflag: tar.TypeReg,
- Mode: 0o644,
- },
- },
- } {
- if err := testBreakout("applylayer", "docker-TestApplyLayerInvalidSymlink", headers); err != nil {
- t.Fatalf("i=%d. %v", i, err)
- }
- }
- }
- func TestApplyLayerWhiteouts(t *testing.T) {
- wd, err := os.MkdirTemp("", "graphdriver-test-whiteouts")
- if err != nil {
- return
- }
- defer os.RemoveAll(wd)
- base := []string{
- ".baz",
- "bar/",
- "bar/bax",
- "bar/bay/",
- "baz",
- "foo/",
- "foo/.abc",
- "foo/.bcd/",
- "foo/.bcd/a",
- "foo/cde/",
- "foo/cde/def",
- "foo/cde/efg",
- "foo/fgh",
- "foobar",
- }
- type tcase struct {
- change, expected []string
- }
- tcases := []tcase{
- {
- base,
- base,
- },
- {
- []string{
- ".bay",
- ".wh.baz",
- "foo/",
- "foo/.bce",
- "foo/.wh..wh..opq",
- "foo/cde/",
- "foo/cde/efg",
- },
- []string{
- ".bay",
- ".baz",
- "bar/",
- "bar/bax",
- "bar/bay/",
- "foo/",
- "foo/.bce",
- "foo/cde/",
- "foo/cde/efg",
- "foobar",
- },
- },
- {
- []string{
- ".bay",
- ".wh..baz",
- ".wh.foobar",
- "foo/",
- "foo/.abc",
- "foo/.wh.cde",
- "bar/",
- },
- []string{
- ".bay",
- "bar/",
- "bar/bax",
- "bar/bay/",
- "foo/",
- "foo/.abc",
- "foo/.bce",
- },
- },
- {
- []string{
- ".abc",
- ".wh..wh..opq",
- "foobar",
- },
- []string{
- ".abc",
- "foobar",
- },
- },
- }
- for i, tc := range tcases {
- l, err := makeTestLayer(tc.change)
- if err != nil {
- t.Fatal(err)
- }
- _, err = UnpackLayer(wd, l, nil)
- if err != nil {
- t.Fatal(err)
- }
- err = l.Close()
- if err != nil {
- t.Fatal(err)
- }
- paths, err := readDirContents(wd)
- if err != nil {
- t.Fatal(err)
- }
- if !reflect.DeepEqual(tc.expected, paths) {
- t.Fatalf("invalid files for layer %d: expected %q, got %q", i, tc.expected, paths)
- }
- }
- }
- func makeTestLayer(paths []string) (rc io.ReadCloser, err error) {
- tmpDir, err := os.MkdirTemp("", "graphdriver-test-mklayer")
- if err != nil {
- return
- }
- defer func() {
- if err != nil {
- os.RemoveAll(tmpDir)
- }
- }()
- for _, p := range paths {
- // Source files are always in Unix format. But we use filepath on
- // creation to be platform agnostic.
- if p[len(p)-1] == '/' {
- if err = os.MkdirAll(filepath.Join(tmpDir, p), 0o700); err != nil {
- return
- }
- } else {
- if err = os.WriteFile(filepath.Join(tmpDir, p), nil, 0o600); err != nil {
- return
- }
- }
- }
- archive, err := Tar(tmpDir, Uncompressed)
- if err != nil {
- return
- }
- return ioutils.NewReadCloserWrapper(archive, func() error {
- err := archive.Close()
- os.RemoveAll(tmpDir)
- return err
- }), nil
- }
- func readDirContents(root string) ([]string, error) {
- var files []string
- err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
- if err != nil {
- return err
- }
- if path == root {
- return nil
- }
- rel, err := filepath.Rel(root, path)
- if err != nil {
- return err
- }
- if info.IsDir() {
- rel = rel + string(filepath.Separator)
- }
- // Append in Unix semantics
- files = append(files, filepath.ToSlash(rel))
- return nil
- })
- if err != nil {
- return nil, err
- }
- return files, nil
- }
|