From 4f28f4492cf5ee3a0ec64ea4e2e01031abfdf490 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Wed, 9 Sep 2026 17:09:28 +0100 Subject: [PATCH] netstorage: stop a path starting with // moving the remote to another host GHSA-xqmq-v4wj-rw5m CVE-PENDING The netstorage backend joins the path after `remote:` onto the configured host URL according to the URL standard. A path beginning with `//` is a network-path reference under that standard, so a path such as `//other.example/dir/` replaced the configured host (and port and userinfo) rather than being treated as a path. Every request, including the initial stat, was then signed with the account secret and sent to the substituted host. The signature covers the request path and action but not the host, so the substituted host received signatures valid for replay against the real NetStorage endpoint for the duration of their validity. Use rest.URLJoinRoot, which refuses any root that changes the scheme, host or userinfo of the configured URL. --- backend/netstorage/netstorage.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/netstorage/netstorage.go b/backend/netstorage/netstorage.go index cbc029ad9..b47eb40f2 100755 --- a/backend/netstorage/netstorage.go +++ b/backend/netstorage/netstorage.go @@ -236,9 +236,9 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e if err != nil { return nil, fmt.Errorf("couldn't parse URL %q: %w", opt.Endpoint, err) } - u, err := rest.URLJoin(base, rest.URLPathEscape(root)) + u, err := rest.URLJoinRoot(base, root) if err != nil { - return nil, fmt.Errorf("couldn't join URL %q and %q: %w", base.String(), root, err) + return nil, err } client := fshttp.NewClient(ctx)