Files
rclone/fs/rc/cache.go
Nick Craig-Wood f191448b0d rc: flip auth default so all endpoints require auth unless opted out
Replace AuthRequired bool with NoAuth bool on the rc.Call struct and
flip the auth check logic. Previously endpoints were unauthenticated
by default and had to opt in with AuthRequired: true, which led to
security vulnerabilities when developers forgot to set the flag.

Now all endpoints require authentication by default. Only explicitly
safe read-only endpoints are marked with NoAuth: true:

- rc/noop
- rc/error
- rc/list
- core/version
- core/stats
- core/group-list
- core/transferred
- core/du
- cache/stats
- vfs/list
- vfs/stats
- vfs/queue
- job/status
- job/list

See GHSA-25qr-6mpr-f7qx, GHSA-jfwf-28xr-xw6q
2026-04-19 13:31:27 +01:00

176 lines
4.6 KiB
Go

// Utilities for accessing the Fs cache
package rc
import (
"context"
"errors"
"fmt"
"github.com/rclone/rclone/fs"
"github.com/rclone/rclone/fs/cache"
"github.com/rclone/rclone/fs/config/configmap"
"github.com/rclone/rclone/fs/filter"
"github.com/rclone/rclone/fs/fspath"
)
// getFsName gets an fs name from fsName either from the cache or direct
func getFsName(in Params, fsName string) (fsString string, err error) {
fsString, err = in.GetString(fsName)
if err != nil {
if !IsErrParamInvalid(err) {
return fsString, err
}
fsString, err = getConfigMap(in, fsName)
if err != nil {
return fsString, err
}
}
return fsString, err
}
// GetFsNamed gets an fs.Fs named fsName either from the cache or creates it afresh
func GetFsNamed(ctx context.Context, in Params, fsName string) (f fs.Fs, err error) {
fsString, err := getFsName(in, fsName)
if err != nil {
return nil, err
}
return cache.Get(ctx, fsString)
}
// GetFsNamedFileOK gets an fs.Fs named fsName either from the cache or creates it afresh
//
// If the fs.Fs points to a single file then it returns a new ctx with
// filters applied to make the listings return only that file.
func GetFsNamedFileOK(ctx context.Context, in Params, fsName string) (newCtx context.Context, f fs.Fs, err error) {
fsString, err := getFsName(in, fsName)
if err != nil {
return ctx, nil, err
}
f, err = cache.Get(ctx, fsString)
if err == nil {
return ctx, f, nil
} else if !errors.Is(err, fs.ErrorIsFile) {
return ctx, nil, err
}
// f points to the directory above the file so find the remote name
_, fileName, err := fspath.Split(fsString)
if err != nil {
return ctx, f, err
}
ctx, fi := filter.AddConfig(ctx)
if !fi.InActive() {
return ctx, f, fmt.Errorf("can't limit to single files when using filters: %q", fileName)
}
// Limit transfers to this file
err = fi.AddFile(fileName)
if err != nil {
return ctx, f, fmt.Errorf("failed to limit to single file: %w", err)
}
return ctx, f, nil
}
// getConfigMap gets the config as a map from in and converts it to a
// config string
//
// It uses the special parameters _name to name the remote and _root
// to make the root of the remote.
func getConfigMap(in Params, fsName string) (fsString string, err error) {
var m configmap.Simple
err = in.GetStruct(fsName, &m)
if err != nil {
return fsString, err
}
pop := func(key string) string {
value := m[key]
delete(m, key)
return value
}
Type := pop("type")
name := pop("_name")
root := pop("_root")
if name != "" {
fsString = name
} else if Type != "" {
fsString = ":" + Type
} else {
return fsString, errors.New(`couldn't find "type" or "_name" in JSON config definition`)
}
config := m.String()
if config != "" {
fsString += ","
fsString += config
}
fsString += ":"
fsString += root
return fsString, nil
}
// GetFs gets an fs.Fs named "fs" either from the cache or creates it afresh
func GetFs(ctx context.Context, in Params) (f fs.Fs, err error) {
return GetFsNamed(ctx, in, "fs")
}
// GetFsAndRemoteNamed gets the fsName parameter from in, makes a
// remote or fetches it from the cache then gets the remoteName
// parameter from in too.
func GetFsAndRemoteNamed(ctx context.Context, in Params, fsName, remoteName string) (f fs.Fs, remote string, err error) {
remote, err = in.GetString(remoteName)
if err != nil {
return
}
f, err = GetFsNamed(ctx, in, fsName)
return
}
// GetFsAndRemote gets the `fs` parameter from in, makes a remote or
// fetches it from the cache then gets the `remote` parameter from in
// too.
func GetFsAndRemote(ctx context.Context, in Params) (f fs.Fs, remote string, err error) {
return GetFsAndRemoteNamed(ctx, in, "fs", "remote")
}
func init() {
Add(Call{
Path: "fscache/clear",
Fn: rcCacheClear,
Title: "Clear the Fs cache.",
Help: `
This clears the fs cache. This is where remotes created from backends
are cached for a short while to make repeated rc calls more efficient.
If you change the parameters of a backend then you may want to call
this to clear an existing remote out of the cache before re-creating
it.
`,
})
}
// Clear the fs cache
func rcCacheClear(ctx context.Context, in Params) (out Params, err error) {
cache.Clear()
return nil, nil
}
func init() {
Add(Call{
Path: "fscache/entries",
Fn: rcCacheEntries,
Title: "Returns the number of entries in the fs cache.",
Help: `
This returns the number of entries in the fs cache.
Returns
- entries - number of items in the cache
`,
})
}
// Return the Entries the fs cache
func rcCacheEntries(ctx context.Context, in Params) (out Params, err error) {
return Params{
"entries": cache.Entries(),
}, nil
}