Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion pkg/blobs/local_storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ func (l *LocalStorage) prependExternalIODir(path string) (string, error) {
}

func (l *LocalStorage) ensureContained(realPath, inputPath string) error {
if !strings.HasPrefix(realPath, l.externalIODir) {
// Compare on path boundaries rather than raw string prefixes: the latter
// also accepts siblings of the I/O directory whose name merely starts with
// it, e.g. "/data/backups-archive" for an I/O directory of "/data/backups".
rel, err := filepath.Rel(l.externalIODir, realPath)
if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
return errors.Errorf("local file access to paths outside of external-io-dir is not allowed: %s", inputPath)
}
return nil
Expand Down
44 changes: 44 additions & 0 deletions pkg/blobs/local_storage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestDirectoryNormalization(t *testing.T) {
Expand All @@ -24,3 +25,46 @@ func TestDirectoryNormalization(t *testing.T) {

assert.Equal(t, expected, l.externalIODir)
}

func TestPrependExternalIODir(t *testing.T) {
externalIODir := filepath.Join(t.TempDir(), "backups")
l, err := NewLocalStorage(externalIODir)
if err != nil {
t.Fatal(err)
}

for _, tc := range []struct {
name string
path string
expectedErr string
}{
{
name: "inside",
path: "test/file.csv",
},
{
name: "parent",
path: "../file.csv",
expectedErr: "outside of external-io-dir is not allowed",
},
{
name: "sibling with the io-dir as a name prefix",
path: "../backups-archive/file.csv",
expectedErr: "outside of external-io-dir is not allowed",
},
{
name: "rooted sibling with the io-dir as a name prefix",
path: "/../backups-archive/file.csv",
expectedErr: "outside of external-io-dir is not allowed",
},
} {
t.Run(tc.name, func(t *testing.T) {
_, err := l.prependExternalIODir(tc.path)
if tc.expectedErr != "" {
require.ErrorContains(t, err, tc.expectedErr)
} else {
require.NoError(t, err)
}
})
}
}