github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/syft/internal/fileresolver/chroot_context_test.go (about)

     1  package fileresolver
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  )
    11  
    12  func Test_ChrootContext_RequestResponse(t *testing.T) {
    13  	// /
    14  	//   somewhere/
    15  	//     outside.txt
    16  	//   root-link -> ./
    17  	//   path/
    18  	//     to/
    19  	//       abs-inside.txt -> /path/to/the/file.txt               # absolute link to somewhere inside of the root
    20  	//       rel-inside.txt -> ./the/file.txt                      # relative link to somewhere inside of the root
    21  	//       the/
    22  	//		   file.txt
    23  	//         abs-outside.txt -> /somewhere/outside.txt           # absolute link to outside of the root
    24  	//         rel-outside -> ../../../somewhere/outside.txt       # relative link to outside of the root
    25  	//
    26  
    27  	testDir, err := os.Getwd()
    28  	require.NoError(t, err)
    29  	relative := filepath.Join("test-fixtures", "req-resp")
    30  	absolute := filepath.Join(testDir, relative)
    31  
    32  	absPathToTheFile := filepath.Join(absolute, "path", "to", "the", "file.txt")
    33  
    34  	absAbsInsidePath := filepath.Join(absolute, "path", "to", "abs-inside.txt")
    35  	absAbsOutsidePath := filepath.Join(absolute, "path", "to", "the", "abs-outside.txt")
    36  
    37  	absRelOutsidePath := filepath.Join(absolute, "path", "to", "the", "rel-outside.txt")
    38  
    39  	relViaLink := filepath.Join(relative, "root-link")
    40  	absViaLink := filepath.Join(absolute, "root-link")
    41  
    42  	absViaLinkPathToTheFile := filepath.Join(absViaLink, "path", "to", "the", "file.txt")
    43  	absViaLinkAbsOutsidePath := filepath.Join(absViaLink, "path", "to", "the", "abs-outside.txt")
    44  	absViaLinkRelOutsidePath := filepath.Join(absViaLink, "path", "to", "the", "rel-outside.txt")
    45  
    46  	relViaDoubleLink := filepath.Join(relative, "root-link", "root-link")
    47  	absViaDoubleLink := filepath.Join(absolute, "root-link", "root-link")
    48  
    49  	absViaDoubleLinkPathToTheFile := filepath.Join(absViaDoubleLink, "path", "to", "the", "file.txt")
    50  	absViaDoubleLinkRelOutsidePath := filepath.Join(absViaDoubleLink, "path", "to", "the", "rel-outside.txt")
    51  
    52  	cleanup := func() {
    53  		_ = os.Remove(absAbsInsidePath)
    54  		_ = os.Remove(absAbsOutsidePath)
    55  	}
    56  
    57  	// ensure the absolute symlinks are cleaned up from any previous runs
    58  	cleanup()
    59  
    60  	require.NoError(t, os.Symlink(filepath.Join(absolute, "path", "to", "the", "file.txt"), absAbsInsidePath))
    61  	require.NoError(t, os.Symlink(filepath.Join(absolute, "somewhere", "outside.txt"), absAbsOutsidePath))
    62  
    63  	t.Cleanup(cleanup)
    64  
    65  	cases := []struct {
    66  		name               string
    67  		cwd                string
    68  		root               string
    69  		base               string
    70  		input              string
    71  		expectedNativePath string
    72  		expectedChrootPath string
    73  	}{
    74  		{
    75  			name:               "relative root, relative request, direct",
    76  			root:               relative,
    77  			input:              "path/to/the/file.txt",
    78  			expectedNativePath: absPathToTheFile,
    79  			expectedChrootPath: "path/to/the/file.txt",
    80  		},
    81  		{
    82  			name:               "abs root, relative request, direct",
    83  			root:               absolute,
    84  			input:              "path/to/the/file.txt",
    85  			expectedNativePath: absPathToTheFile,
    86  			expectedChrootPath: "path/to/the/file.txt",
    87  		},
    88  		{
    89  			name:               "relative root, abs request, direct",
    90  			root:               relative,
    91  			input:              "/path/to/the/file.txt",
    92  			expectedNativePath: absPathToTheFile,
    93  			expectedChrootPath: "path/to/the/file.txt",
    94  		},
    95  		{
    96  			name:               "abs root, abs request, direct",
    97  			root:               absolute,
    98  			input:              "/path/to/the/file.txt",
    99  			expectedNativePath: absPathToTheFile,
   100  			expectedChrootPath: "path/to/the/file.txt",
   101  		},
   102  		// cwd within root...
   103  		{
   104  			name:               "relative root, relative request, direct, cwd within root",
   105  			cwd:                filepath.Join(relative, "path/to"),
   106  			root:               "../../",
   107  			input:              "path/to/the/file.txt",
   108  			expectedNativePath: absPathToTheFile,
   109  			expectedChrootPath: "path/to/the/file.txt",
   110  		},
   111  		{
   112  			name:               "abs root, relative request, direct, cwd within root",
   113  			cwd:                filepath.Join(relative, "path/to"),
   114  			root:               absolute,
   115  			input:              "path/to/the/file.txt",
   116  			expectedNativePath: absPathToTheFile,
   117  			expectedChrootPath: "path/to/the/file.txt",
   118  		},
   119  		{
   120  			name:               "relative root, abs request, direct, cwd within root",
   121  			cwd:                filepath.Join(relative, "path/to"),
   122  			root:               "../../",
   123  			input:              "/path/to/the/file.txt",
   124  			expectedNativePath: absPathToTheFile,
   125  			expectedChrootPath: "path/to/the/file.txt",
   126  		},
   127  		{
   128  			name: "abs root, abs request, direct, cwd within root",
   129  			cwd:  filepath.Join(relative, "path/to"),
   130  
   131  			root:               absolute,
   132  			input:              "/path/to/the/file.txt",
   133  			expectedNativePath: absPathToTheFile,
   134  			expectedChrootPath: "path/to/the/file.txt",
   135  		},
   136  		// cwd within symlink root...
   137  		{
   138  			name:               "relative root, relative request, direct, cwd within symlink root",
   139  			cwd:                relViaLink,
   140  			root:               "./",
   141  			input:              "path/to/the/file.txt",
   142  			expectedNativePath: absViaLinkPathToTheFile,
   143  			expectedChrootPath: "path/to/the/file.txt",
   144  		},
   145  		{
   146  			name:               "abs root, relative request, direct, cwd within symlink root",
   147  			cwd:                relViaLink,
   148  			root:               absViaLink,
   149  			input:              "path/to/the/file.txt",
   150  			expectedNativePath: absPathToTheFile,
   151  			expectedChrootPath: "path/to/the/file.txt",
   152  		},
   153  		{
   154  			name:               "relative root, abs request, direct, cwd within symlink root",
   155  			cwd:                relViaLink,
   156  			root:               "./",
   157  			input:              "/path/to/the/file.txt",
   158  			expectedNativePath: absViaLinkPathToTheFile,
   159  			expectedChrootPath: "path/to/the/file.txt",
   160  		},
   161  		{
   162  			name:               "abs root, abs request, direct, cwd within symlink root",
   163  			cwd:                relViaLink,
   164  			root:               absViaLink,
   165  			input:              "/path/to/the/file.txt",
   166  			expectedNativePath: absPathToTheFile,
   167  			expectedChrootPath: "path/to/the/file.txt",
   168  		},
   169  		// cwd within symlink root, request nested within...
   170  		{
   171  			name:               "relative root, relative nested request, direct, cwd within symlink root",
   172  			cwd:                relViaLink,
   173  			root:               "./path",
   174  			input:              "to/the/file.txt",
   175  			expectedNativePath: absViaLinkPathToTheFile,
   176  			expectedChrootPath: "to/the/file.txt",
   177  		},
   178  		{
   179  			name:               "abs root, relative nested request, direct, cwd within symlink root",
   180  			cwd:                relViaLink,
   181  			root:               filepath.Join(absViaLink, "path"),
   182  			input:              "to/the/file.txt",
   183  			expectedNativePath: absPathToTheFile,
   184  			expectedChrootPath: "to/the/file.txt",
   185  		},
   186  		{
   187  			name:               "relative root, abs nested request, direct, cwd within symlink root",
   188  			cwd:                relViaLink,
   189  			root:               "./path",
   190  			input:              "/to/the/file.txt",
   191  			expectedNativePath: absViaLinkPathToTheFile,
   192  			expectedChrootPath: "to/the/file.txt",
   193  		},
   194  		{
   195  			name:               "abs root, abs nested request, direct, cwd within symlink root",
   196  			cwd:                relViaLink,
   197  			root:               filepath.Join(absViaLink, "path"),
   198  			input:              "/to/the/file.txt",
   199  			expectedNativePath: absPathToTheFile,
   200  			expectedChrootPath: "to/the/file.txt",
   201  		},
   202  		// cwd within DOUBLE symlink root...
   203  		{
   204  			name:               "relative root, relative request, direct, cwd within (double) symlink root",
   205  			cwd:                relViaDoubleLink,
   206  			root:               "./",
   207  			input:              "path/to/the/file.txt",
   208  			expectedNativePath: absViaDoubleLinkPathToTheFile,
   209  			expectedChrootPath: "path/to/the/file.txt",
   210  		},
   211  		{
   212  			name:               "abs root, relative request, direct, cwd within (double) symlink root",
   213  			cwd:                relViaDoubleLink,
   214  			root:               absViaDoubleLink,
   215  			input:              "path/to/the/file.txt",
   216  			expectedNativePath: absPathToTheFile,
   217  			expectedChrootPath: "path/to/the/file.txt",
   218  		},
   219  		{
   220  			name:               "relative root, abs request, direct, cwd within (double) symlink root",
   221  			cwd:                relViaDoubleLink,
   222  			root:               "./",
   223  			input:              "/path/to/the/file.txt",
   224  			expectedNativePath: absViaDoubleLinkPathToTheFile,
   225  			expectedChrootPath: "path/to/the/file.txt",
   226  		},
   227  		{
   228  			name:               "abs root, abs request, direct, cwd within (double) symlink root",
   229  			cwd:                relViaDoubleLink,
   230  			root:               absViaDoubleLink,
   231  			input:              "/path/to/the/file.txt",
   232  			expectedNativePath: absPathToTheFile,
   233  			expectedChrootPath: "path/to/the/file.txt",
   234  		},
   235  		// cwd within DOUBLE symlink root, request nested within...
   236  		{
   237  			name:               "relative root, relative nested request, direct, cwd within (double) symlink root",
   238  			cwd:                relViaDoubleLink,
   239  			root:               "./path",
   240  			input:              "to/the/file.txt",
   241  			expectedNativePath: absViaDoubleLinkPathToTheFile,
   242  			expectedChrootPath: "to/the/file.txt",
   243  		},
   244  		{
   245  			name:               "abs root, relative nested request, direct, cwd within (double) symlink root",
   246  			cwd:                relViaDoubleLink,
   247  			root:               filepath.Join(absViaDoubleLink, "path"),
   248  			input:              "to/the/file.txt",
   249  			expectedNativePath: absPathToTheFile,
   250  			expectedChrootPath: "to/the/file.txt",
   251  		},
   252  		{
   253  			name:               "relative root, abs nested request, direct, cwd within (double) symlink root",
   254  			cwd:                relViaDoubleLink,
   255  			root:               "./path",
   256  			input:              "/to/the/file.txt",
   257  			expectedNativePath: absViaDoubleLinkPathToTheFile,
   258  			expectedChrootPath: "to/the/file.txt",
   259  		},
   260  		{
   261  			name:               "abs root, abs nested request, direct, cwd within (double) symlink root",
   262  			cwd:                relViaDoubleLink,
   263  			root:               filepath.Join(absViaDoubleLink, "path"),
   264  			input:              "/to/the/file.txt",
   265  			expectedNativePath: absPathToTheFile,
   266  			expectedChrootPath: "to/the/file.txt",
   267  		},
   268  		// cwd within DOUBLE symlink root, request nested DEEP within...
   269  		{
   270  			name:               "relative root, relative nested request, direct, cwd deep within (double) symlink root",
   271  			cwd:                filepath.Join(relViaDoubleLink, "path", "to"),
   272  			root:               "../",
   273  			input:              "to/the/file.txt",
   274  			expectedNativePath: absViaDoubleLinkPathToTheFile,
   275  			expectedChrootPath: "to/the/file.txt",
   276  		},
   277  		{
   278  			name:               "abs root, relative nested request, direct, cwd deep within (double) symlink root",
   279  			cwd:                filepath.Join(relViaDoubleLink, "path", "to"),
   280  			root:               filepath.Join(absViaDoubleLink, "path"),
   281  			input:              "to/the/file.txt",
   282  			expectedNativePath: absPathToTheFile,
   283  			expectedChrootPath: "to/the/file.txt",
   284  		},
   285  		{
   286  			name:               "relative root, abs nested request, direct, cwd deep within (double) symlink root",
   287  			cwd:                filepath.Join(relViaDoubleLink, "path", "to"),
   288  			root:               "../",
   289  			input:              "/to/the/file.txt",
   290  			expectedNativePath: absViaDoubleLinkPathToTheFile,
   291  			expectedChrootPath: "to/the/file.txt",
   292  		},
   293  		{
   294  			name:               "abs root, abs nested request, direct, cwd deep within (double) symlink root",
   295  			cwd:                filepath.Join(relViaDoubleLink, "path", "to"),
   296  			root:               filepath.Join(absViaDoubleLink, "path"),
   297  			input:              "/to/the/file.txt",
   298  			expectedNativePath: absPathToTheFile,
   299  			expectedChrootPath: "to/the/file.txt",
   300  		},
   301  		// link to outside of root cases...
   302  		{
   303  			name:               "relative root, relative request, abs indirect (outside of root)",
   304  			root:               filepath.Join(relative, "path"),
   305  			input:              "to/the/abs-outside.txt",
   306  			expectedNativePath: absAbsOutsidePath,
   307  			expectedChrootPath: "to/the/abs-outside.txt",
   308  		},
   309  		{
   310  			name:               "abs root, relative request, abs indirect (outside of root)",
   311  			root:               filepath.Join(absolute, "path"),
   312  			input:              "to/the/abs-outside.txt",
   313  			expectedNativePath: absAbsOutsidePath,
   314  			expectedChrootPath: "to/the/abs-outside.txt",
   315  		},
   316  		{
   317  			name:               "relative root, abs request, abs indirect (outside of root)",
   318  			root:               filepath.Join(relative, "path"),
   319  			input:              "/to/the/abs-outside.txt",
   320  			expectedNativePath: absAbsOutsidePath,
   321  			expectedChrootPath: "to/the/abs-outside.txt",
   322  		},
   323  		{
   324  			name:               "abs root, abs request, abs indirect (outside of root)",
   325  			root:               filepath.Join(absolute, "path"),
   326  			input:              "/to/the/abs-outside.txt",
   327  			expectedNativePath: absAbsOutsidePath,
   328  			expectedChrootPath: "to/the/abs-outside.txt",
   329  		},
   330  		{
   331  			name:               "relative root, relative request, relative indirect (outside of root)",
   332  			root:               filepath.Join(relative, "path"),
   333  			input:              "to/the/rel-outside.txt",
   334  			expectedNativePath: absRelOutsidePath,
   335  			expectedChrootPath: "to/the/rel-outside.txt",
   336  		},
   337  		{
   338  			name:               "abs root, relative request, relative indirect (outside of root)",
   339  			root:               filepath.Join(absolute, "path"),
   340  			input:              "to/the/rel-outside.txt",
   341  			expectedNativePath: absRelOutsidePath,
   342  			expectedChrootPath: "to/the/rel-outside.txt",
   343  		},
   344  		{
   345  			name:               "relative root, abs request, relative indirect (outside of root)",
   346  			root:               filepath.Join(relative, "path"),
   347  			input:              "/to/the/rel-outside.txt",
   348  			expectedNativePath: absRelOutsidePath,
   349  			expectedChrootPath: "to/the/rel-outside.txt",
   350  		},
   351  		{
   352  			name:               "abs root, abs request, relative indirect (outside of root)",
   353  			root:               filepath.Join(absolute, "path"),
   354  			input:              "/to/the/rel-outside.txt",
   355  			expectedNativePath: absRelOutsidePath,
   356  			expectedChrootPath: "to/the/rel-outside.txt",
   357  		},
   358  		// link to outside of root cases... cwd within symlink root
   359  		{
   360  			name:               "relative root, relative request, abs indirect (outside of root), cwd within symlink root",
   361  			cwd:                relViaLink,
   362  			root:               "path",
   363  			input:              "to/the/abs-outside.txt",
   364  			expectedNativePath: absViaLinkAbsOutsidePath,
   365  			expectedChrootPath: "to/the/abs-outside.txt",
   366  		},
   367  		{
   368  			name:               "abs root, relative request, abs indirect (outside of root), cwd within symlink root",
   369  			cwd:                relViaLink,
   370  			root:               filepath.Join(absolute, "path"),
   371  			input:              "to/the/abs-outside.txt",
   372  			expectedNativePath: absAbsOutsidePath,
   373  			expectedChrootPath: "to/the/abs-outside.txt",
   374  		},
   375  		{
   376  			name:               "relative root, abs request, abs indirect (outside of root), cwd within symlink root",
   377  			cwd:                relViaLink,
   378  			root:               "path",
   379  			input:              "/to/the/abs-outside.txt",
   380  			expectedNativePath: absViaLinkAbsOutsidePath,
   381  			expectedChrootPath: "to/the/abs-outside.txt",
   382  		},
   383  		{
   384  			name:               "abs root, abs request, abs indirect (outside of root), cwd within symlink root",
   385  			cwd:                relViaLink,
   386  			root:               filepath.Join(absolute, "path"),
   387  			input:              "/to/the/abs-outside.txt",
   388  			expectedNativePath: absAbsOutsidePath,
   389  			expectedChrootPath: "to/the/abs-outside.txt",
   390  		},
   391  		{
   392  			name:               "relative root, relative request, relative indirect (outside of root), cwd within symlink root",
   393  			cwd:                relViaLink,
   394  			root:               "path",
   395  			input:              "to/the/rel-outside.txt",
   396  			expectedNativePath: absViaLinkRelOutsidePath,
   397  			expectedChrootPath: "to/the/rel-outside.txt",
   398  		},
   399  		{
   400  			name:               "abs root, relative request, relative indirect (outside of root), cwd within symlink root",
   401  			cwd:                relViaLink,
   402  			root:               filepath.Join(absolute, "path"),
   403  			input:              "to/the/rel-outside.txt",
   404  			expectedNativePath: absRelOutsidePath,
   405  			expectedChrootPath: "to/the/rel-outside.txt",
   406  		},
   407  		{
   408  			name:               "relative root, abs request, relative indirect (outside of root), cwd within symlink root",
   409  			cwd:                relViaLink,
   410  			root:               "path",
   411  			input:              "/to/the/rel-outside.txt",
   412  			expectedNativePath: absViaLinkRelOutsidePath,
   413  			expectedChrootPath: "to/the/rel-outside.txt",
   414  		},
   415  		{
   416  			name:               "abs root, abs request, relative indirect (outside of root), cwd within symlink root",
   417  			cwd:                relViaLink,
   418  			root:               filepath.Join(absolute, "path"),
   419  			input:              "/to/the/rel-outside.txt",
   420  			expectedNativePath: absRelOutsidePath,
   421  			expectedChrootPath: "to/the/rel-outside.txt",
   422  		},
   423  		{
   424  			name:               "relative root, relative request, relative indirect (outside of root), cwd within DOUBLE symlink root",
   425  			cwd:                relViaDoubleLink,
   426  			root:               "path",
   427  			input:              "to/the/rel-outside.txt",
   428  			expectedNativePath: absViaDoubleLinkRelOutsidePath,
   429  			expectedChrootPath: "to/the/rel-outside.txt",
   430  		},
   431  		{
   432  			name:               "abs root, relative request, relative indirect (outside of root), cwd within DOUBLE symlink root",
   433  			cwd:                relViaDoubleLink,
   434  			root:               filepath.Join(absolute, "path"),
   435  			input:              "to/the/rel-outside.txt",
   436  			expectedNativePath: absRelOutsidePath,
   437  			expectedChrootPath: "to/the/rel-outside.txt",
   438  		},
   439  		{
   440  			name:               "relative root, abs request, relative indirect (outside of root), cwd within DOUBLE symlink root",
   441  			cwd:                relViaDoubleLink,
   442  			root:               "path",
   443  			input:              "/to/the/rel-outside.txt",
   444  			expectedNativePath: absViaDoubleLinkRelOutsidePath,
   445  			expectedChrootPath: "to/the/rel-outside.txt",
   446  		},
   447  		{
   448  			name:               "abs root, abs request, relative indirect (outside of root), cwd within DOUBLE symlink root",
   449  			cwd:                relViaDoubleLink,
   450  			root:               filepath.Join(absolute, "path"),
   451  			input:              "/to/the/rel-outside.txt",
   452  			expectedNativePath: absRelOutsidePath,
   453  			expectedChrootPath: "to/the/rel-outside.txt",
   454  		},
   455  	}
   456  	for _, c := range cases {
   457  		t.Run(c.name, func(t *testing.T) {
   458  
   459  			// we need to mimic a shell, otherwise we won't get a path within a symlink
   460  			targetPath := filepath.Join(testDir, c.cwd)
   461  			t.Setenv("PWD", filepath.Clean(targetPath))
   462  
   463  			require.NoError(t, err)
   464  			require.NoError(t, os.Chdir(targetPath))
   465  			t.Cleanup(func() {
   466  				require.NoError(t, os.Chdir(testDir))
   467  			})
   468  
   469  			chroot, err := NewChrootContextFromCWD(c.root, c.base)
   470  			require.NoError(t, err)
   471  			require.NotNil(t, chroot)
   472  
   473  			req, err := chroot.ToNativePath(c.input)
   474  			require.NoError(t, err)
   475  			assert.Equal(t, c.expectedNativePath, req, "native path different")
   476  
   477  			resp := chroot.ToChrootPath(req)
   478  			assert.Equal(t, c.expectedChrootPath, resp, "chroot path different")
   479  		})
   480  	}
   481  }