godev/internal/storage: reject unusual FS object names General security hardening: Run the name provided to NewFSObject through filepath.Localize, which requires that it be a valid io/fs path (no .. components) and also representable on the local filesystem (no opening NUL on Windows). Change-Id: I00e7797e55daf4bfb010a71c0fa2b6446a6a6964 Reviewed-on: https://go-review.googlesource.com/c/telemetry/+/848192 Reviewed-by: Nicholas Husin <nsh@golang.org> Reviewed-by: Nicholas Husin <husin@google.com> Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/godev/internal/storage/storage.go b/godev/internal/storage/storage.go index a96585e..a269114 100644 --- a/godev/internal/storage/storage.go +++ b/godev/internal/storage/storage.go
@@ -166,11 +166,16 @@ type FSObject struct { filename string + err error } func NewFSObject(b *FSBucket, name string) ObjectHandle { - filename := filepath.Join(b.dir, b.bucket, filepath.FromSlash(name)) - return &FSObject{filename} + localName, err := filepath.Localize(name) + if err != nil { + return &FSObject{err: fmt.Errorf("invalid object name %q: %w", name, err)} + } + filename := filepath.Join(b.dir, b.bucket, localName) + return &FSObject{filename: filename} } func (o *FSObject) Filename() string { @@ -178,6 +183,9 @@ } func (o *FSObject) NewReader(ctx context.Context) (io.ReadCloser, error) { + if o.err != nil { + return nil, o.err + } r, err := os.Open(o.filename) if errors.Is(err, os.ErrNotExist) { return nil, ErrObjectNotExist @@ -186,6 +194,9 @@ } func (o *FSObject) NewWriter(ctx context.Context) (io.WriteCloser, error) { + if o.err != nil { + return nil, o.err + } if err := os.MkdirAll(filepath.Dir(o.filename), os.ModePerm); err != nil { return nil, err }
diff --git a/godev/internal/storage/storage_test.go b/godev/internal/storage/storage_test.go index 5fc8eb6..951a8b9 100644 --- a/godev/internal/storage/storage_test.go +++ b/godev/internal/storage/storage_test.go
@@ -32,6 +32,14 @@ t.Fatal(err) } runTest(t, ctx, s) + for _, invalid := range []string{"../escape", "a/../../escape", "/abs"} { + if err := write(ctx, s, invalid, writeData); err == nil { + t.Errorf("write(%q) succeeded, want error", invalid) + } + if _, err := read(ctx, s, invalid); err == nil { + t.Errorf("read(%q) succeeded, want error", invalid) + } + } } func runTest(t *testing.T, ctx context.Context, s BucketHandle) {