github.com/argoproj/argo-cd/v3@v3.2.1/util/io/path/resolved_test.go (about)

     1  package path
     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_resolveSymlinkRecursive(t *testing.T) {
    13  	testsDir, err := filepath.Abs("./testdata")
    14  	if err != nil {
    15  		panic(err)
    16  	}
    17  	t.Run("Resolve non-symlink", func(t *testing.T) {
    18  		r, err := resolveSymbolicLinkRecursive(testsDir+"/foo", 2)
    19  		require.NoError(t, err)
    20  		assert.Equal(t, testsDir+"/foo", r)
    21  	})
    22  	t.Run("Successfully resolve symlink", func(t *testing.T) {
    23  		r, err := resolveSymbolicLinkRecursive(testsDir+"/bar", 2)
    24  		require.NoError(t, err)
    25  		assert.Equal(t, testsDir+"/foo", r)
    26  	})
    27  	t.Run("Do not allow symlink at all", func(t *testing.T) {
    28  		r, err := resolveSymbolicLinkRecursive(testsDir+"/bar", 0)
    29  		require.Error(t, err)
    30  		assert.Empty(t, r)
    31  	})
    32  	t.Run("Error because too nested symlink", func(t *testing.T) {
    33  		r, err := resolveSymbolicLinkRecursive(testsDir+"/bam", 2)
    34  		require.Error(t, err)
    35  		assert.Empty(t, r)
    36  	})
    37  	t.Run("No such file or directory", func(t *testing.T) {
    38  		r, err := resolveSymbolicLinkRecursive(testsDir+"/foobar", 2)
    39  		require.NoError(t, err)
    40  		assert.Equal(t, testsDir+"/foobar", r)
    41  	})
    42  }
    43  
    44  func Test_isURLSchemeAllowed(t *testing.T) {
    45  	type testdata struct {
    46  		name     string
    47  		scheme   string
    48  		allowed  []string
    49  		expected bool
    50  	}
    51  	tts := []testdata{
    52  		{
    53  			name:     "Allowed scheme matches",
    54  			scheme:   "http",
    55  			allowed:  []string{"http", "https"},
    56  			expected: true,
    57  		},
    58  		{
    59  			name:     "Allowed scheme matches only partially",
    60  			scheme:   "http",
    61  			allowed:  []string{"https"},
    62  			expected: false,
    63  		},
    64  		{
    65  			name:     "Scheme is not allowed",
    66  			scheme:   "file",
    67  			allowed:  []string{"http", "https"},
    68  			expected: false,
    69  		},
    70  		{
    71  			name:     "Empty scheme with valid allowances is forbidden",
    72  			scheme:   "",
    73  			allowed:  []string{"http", "https"},
    74  			expected: false,
    75  		},
    76  		{
    77  			name:     "Empty scheme with empty allowances is forbidden",
    78  			scheme:   "",
    79  			allowed:  []string{},
    80  			expected: false,
    81  		},
    82  		{
    83  			name:     "Some scheme with empty allowances is forbidden",
    84  			scheme:   "file",
    85  			allowed:  []string{},
    86  			expected: false,
    87  		},
    88  	}
    89  	for _, tt := range tts {
    90  		t.Run(tt.name, func(t *testing.T) {
    91  			r := isURLSchemeAllowed(tt.scheme, tt.allowed)
    92  			assert.Equal(t, tt.expected, r)
    93  		})
    94  	}
    95  }
    96  
    97  var allowedRemoteProtocols = []string{"http", "https"}
    98  
    99  func Test_resolveFilePath(t *testing.T) {
   100  	t.Run("Resolve normal relative path into absolute path", func(t *testing.T) {
   101  		p, remote, err := ResolveValueFilePathOrUrl("/foo/bar", "/foo", "baz/bim.yaml", allowedRemoteProtocols)
   102  		require.NoError(t, err)
   103  		assert.False(t, remote)
   104  		assert.Equal(t, "/foo/bar/baz/bim.yaml", string(p))
   105  	})
   106  	t.Run("Resolve normal relative path into absolute path", func(t *testing.T) {
   107  		p, remote, err := ResolveValueFilePathOrUrl("/foo/bar", "/foo", "baz/../../bim.yaml", allowedRemoteProtocols)
   108  		require.NoError(t, err)
   109  		assert.False(t, remote)
   110  		assert.Equal(t, "/foo/bim.yaml", string(p))
   111  	})
   112  	t.Run("Error on path resolving outside repository root", func(t *testing.T) {
   113  		p, remote, err := ResolveValueFilePathOrUrl("/foo/bar", "/foo", "baz/../../../bim.yaml", allowedRemoteProtocols)
   114  		require.ErrorContains(t, err, "outside repository root")
   115  		assert.False(t, remote)
   116  		assert.Empty(t, string(p))
   117  	})
   118  	t.Run("Return verbatim URL", func(t *testing.T) {
   119  		url := "https://some.where/foo,yaml"
   120  		p, remote, err := ResolveValueFilePathOrUrl("/foo/bar", "/foo", url, allowedRemoteProtocols)
   121  		require.NoError(t, err)
   122  		assert.True(t, remote)
   123  		assert.Equal(t, url, string(p))
   124  	})
   125  	t.Run("URL scheme not allowed", func(t *testing.T) {
   126  		url := "file:///some.where/foo,yaml"
   127  		p, remote, err := ResolveValueFilePathOrUrl("/foo/bar", "/foo", url, allowedRemoteProtocols)
   128  		require.Error(t, err)
   129  		assert.False(t, remote)
   130  		assert.Empty(t, string(p))
   131  	})
   132  	t.Run("Implicit URL by absolute path", func(t *testing.T) {
   133  		p, remote, err := ResolveValueFilePathOrUrl("/foo/bar", "/foo", "/baz.yaml", allowedRemoteProtocols)
   134  		require.NoError(t, err)
   135  		assert.False(t, remote)
   136  		assert.Equal(t, "/foo/baz.yaml", string(p))
   137  	})
   138  	t.Run("Relative app path", func(t *testing.T) {
   139  		p, remote, err := ResolveValueFilePathOrUrl(".", "/foo", "/baz.yaml", allowedRemoteProtocols)
   140  		require.NoError(t, err)
   141  		assert.False(t, remote)
   142  		assert.Equal(t, "/foo/baz.yaml", string(p))
   143  	})
   144  	t.Run("Relative repo path", func(t *testing.T) {
   145  		c, err := os.Getwd()
   146  		require.NoError(t, err)
   147  		p, remote, err := ResolveValueFilePathOrUrl(".", ".", "baz.yaml", allowedRemoteProtocols)
   148  		require.NoError(t, err)
   149  		assert.False(t, remote)
   150  		assert.Equal(t, c+"/baz.yaml", string(p))
   151  	})
   152  	t.Run("Overlapping root prefix without trailing slash", func(t *testing.T) {
   153  		p, remote, err := ResolveValueFilePathOrUrl(".", "/foo", "../foo2/baz.yaml", allowedRemoteProtocols)
   154  		require.ErrorContains(t, err, "outside repository root")
   155  		assert.False(t, remote)
   156  		assert.Empty(t, string(p))
   157  	})
   158  	t.Run("Overlapping root prefix with trailing slash", func(t *testing.T) {
   159  		p, remote, err := ResolveValueFilePathOrUrl(".", "/foo/", "../foo2/baz.yaml", allowedRemoteProtocols)
   160  		require.ErrorContains(t, err, "outside repository root")
   161  		assert.False(t, remote)
   162  		assert.Empty(t, string(p))
   163  	})
   164  	t.Run("Garbage input as values file", func(t *testing.T) {
   165  		p, remote, err := ResolveValueFilePathOrUrl(".", "/foo/", "kfdj\\ks&&&321209.,---e32908923%$ยง!\"", allowedRemoteProtocols)
   166  		require.ErrorContains(t, err, "outside repository root")
   167  		assert.False(t, remote)
   168  		assert.Empty(t, string(p))
   169  	})
   170  	t.Run("NUL-byte path input as values file", func(t *testing.T) {
   171  		p, remote, err := ResolveValueFilePathOrUrl(".", "/foo/", "\000", allowedRemoteProtocols)
   172  		require.ErrorContains(t, err, "outside repository root")
   173  		assert.False(t, remote)
   174  		assert.Empty(t, string(p))
   175  	})
   176  	t.Run("Resolve root path into absolute path - jsonnet library path", func(t *testing.T) {
   177  		p, err := ResolveFileOrDirectoryPath("/foo", "/foo", "./")
   178  		require.NoError(t, err)
   179  		assert.Equal(t, "/foo", string(p))
   180  	})
   181  }