github.com/rawahars/moby@v24.0.4+incompatible/pkg/archive/diff_test.go (about)

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