Before this change the environment variable getters in fs/configmap.go logged the option value with %q, so a password set via RCLONE_CONFIG_remote_pass (or RCLONE_remote_pass) was printed in full to the debug log. Values from the config file were already redacted, which made the leak easy to miss. This change routes both getters through fs.RedactOptionValue, which looks up the option in the backend's option list: options marked IsPassword or Sensitive log as XXX, unknown options are conservatively redacted, and --dump auth still shows the value for debugging. Fixes #5794
94 lines
2.6 KiB
Go
94 lines
2.6 KiB
Go
package fs
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"log/slog"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
// captureLog redirects the fs logger into a buffer and enables debug
|
|
// logging for the duration of the test.
|
|
func captureLog(t *testing.T) *bytes.Buffer {
|
|
var buf bytes.Buffer
|
|
oldLogger := logger
|
|
t.Cleanup(func() { logger = oldLogger })
|
|
SetLogger(slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug}))
|
|
ci := GetConfig(context.Background())
|
|
oldLevel := ci.LogLevel
|
|
t.Cleanup(func() { ci.LogLevel = oldLevel })
|
|
ci.LogLevel = LogLevelDebug
|
|
return &buf
|
|
}
|
|
|
|
// TestConfigEnvVarsRedactsPassword checks that the value of a
|
|
// password config option set via the environment
|
|
// (RCLONE_CONFIG_backend_option) is not logged (see #5794).
|
|
func TestConfigEnvVarsRedactsPassword(t *testing.T) {
|
|
t.Setenv("RCLONE_CONFIG_SRC_PASS", "obscured_pass")
|
|
t.Setenv("RCLONE_CONFIG_SRC_HOST", "example.com")
|
|
buf := captureLog(t)
|
|
|
|
config := configEnvVars{
|
|
configName: "src",
|
|
options: Options{
|
|
{Name: "pass", IsPassword: true},
|
|
{Name: "host"},
|
|
},
|
|
}
|
|
|
|
// The value is still returned to the caller...
|
|
value, ok := config.Get("pass")
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "obscured_pass", value)
|
|
// ...but not logged
|
|
assert.NotContains(t, buf.String(), "obscured_pass")
|
|
|
|
// Non sensitive values are still logged
|
|
buf.Reset()
|
|
value, ok = config.Get("host")
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "example.com", value)
|
|
assert.Contains(t, buf.String(), "example.com")
|
|
}
|
|
|
|
// TestConfigEnvVarsDumpAuthShowsPassword checks that with --dump auth
|
|
// set the password value is still shown in the log for debugging.
|
|
func TestConfigEnvVarsDumpAuthShowsPassword(t *testing.T) {
|
|
t.Setenv("RCLONE_CONFIG_SRC_PASS", "obscured_pass")
|
|
buf := captureLog(t)
|
|
ci := GetConfig(context.Background())
|
|
oldDump := ci.Dump
|
|
t.Cleanup(func() { ci.Dump = oldDump })
|
|
ci.Dump = DumpAuth
|
|
|
|
config := configEnvVars{
|
|
configName: "src",
|
|
options: Options{
|
|
{Name: "pass", IsPassword: true},
|
|
},
|
|
}
|
|
_, ok := config.Get("pass")
|
|
assert.True(t, ok)
|
|
assert.Contains(t, buf.String(), "obscured_pass")
|
|
}
|
|
|
|
// TestOptionEnvVarsRedactsPassword checks that the value of a
|
|
// password option set via the environment (RCLONE_backend_option) is
|
|
// not logged (see #5794).
|
|
func TestOptionEnvVarsRedactsPassword(t *testing.T) {
|
|
t.Setenv("RCLONE_SFTP_PASS", "obscured_pass")
|
|
buf := captureLog(t)
|
|
|
|
oev := optionEnvVars{
|
|
prefix: "sftp",
|
|
options: Options{{Name: "pass", IsPassword: true}},
|
|
}
|
|
value, ok := oev.Get("pass")
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "obscured_pass", value)
|
|
assert.NotContains(t, buf.String(), "obscured_pass")
|
|
}
|