mirror of
https://github.com/restic/restic.git
synced 2025-08-12 15:07:53 +00:00
Merge pull request #2658 from creativeprojects/issue-2241
Don't echo authentication passwords (rest backend)
This commit is contained in:
96
internal/backend/location/display_location_test.go
Normal file
96
internal/backend/location/display_location_test.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package location
|
||||
|
||||
import "testing"
|
||||
|
||||
var passwordTests = []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
"local:/srv/repo",
|
||||
"local:/srv/repo",
|
||||
},
|
||||
{
|
||||
"/dir1/dir2",
|
||||
"/dir1/dir2",
|
||||
},
|
||||
{
|
||||
`c:\dir1\foobar\dir2`,
|
||||
`c:\dir1\foobar\dir2`,
|
||||
},
|
||||
{
|
||||
"sftp:user@host:/srv/repo",
|
||||
"sftp:user@host:/srv/repo",
|
||||
},
|
||||
{
|
||||
"s3://eu-central-1/bucketname",
|
||||
"s3://eu-central-1/bucketname",
|
||||
},
|
||||
{
|
||||
"swift:container17:/prefix97",
|
||||
"swift:container17:/prefix97",
|
||||
},
|
||||
{
|
||||
"b2:bucketname:/prefix",
|
||||
"b2:bucketname:/prefix",
|
||||
},
|
||||
{
|
||||
"rest:",
|
||||
"rest:/",
|
||||
},
|
||||
{
|
||||
"rest:localhost/",
|
||||
"rest:localhost/",
|
||||
},
|
||||
{
|
||||
"rest::123/",
|
||||
"rest::123/",
|
||||
},
|
||||
{
|
||||
"rest:http://",
|
||||
"rest:http://",
|
||||
},
|
||||
{
|
||||
"rest:http://hostname.foo:1234/",
|
||||
"rest:http://hostname.foo:1234/",
|
||||
},
|
||||
{
|
||||
"rest:http://user@hostname.foo:1234/",
|
||||
"rest:http://user@hostname.foo:1234/",
|
||||
},
|
||||
{
|
||||
"rest:http://user:@hostname.foo:1234/",
|
||||
"rest:http://user:***@hostname.foo:1234/",
|
||||
},
|
||||
{
|
||||
"rest:http://user:p@hostname.foo:1234/",
|
||||
"rest:http://user:***@hostname.foo:1234/",
|
||||
},
|
||||
{
|
||||
"rest:http://user:pppppaaafhhfuuwiiehhthhghhdkjaoowpprooghjjjdhhwuuhgjsjhhfdjhruuhsjsdhhfhshhsppwufhhsjjsjs@hostname.foo:1234/",
|
||||
"rest:http://user:***@hostname.foo:1234/",
|
||||
},
|
||||
{
|
||||
"rest:http://user:password@hostname",
|
||||
"rest:http://user:***@hostname/",
|
||||
},
|
||||
{
|
||||
"rest:http://user:password@:123",
|
||||
"rest:http://user:***@:123/",
|
||||
},
|
||||
{
|
||||
"rest:http://user:password@",
|
||||
"rest:http://user:***@/",
|
||||
},
|
||||
}
|
||||
|
||||
func TestStripPassword(t *testing.T) {
|
||||
for i, test := range passwordTests {
|
||||
t.Run(test.input, func(t *testing.T) {
|
||||
result := StripPassword(test.input)
|
||||
if result != test.expected {
|
||||
t.Errorf("test %d: expected '%s' but got '%s'", i, test.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@@ -24,22 +24,28 @@ type Location struct {
|
||||
}
|
||||
|
||||
type parser struct {
|
||||
scheme string
|
||||
parse func(string) (interface{}, error)
|
||||
scheme string
|
||||
parse func(string) (interface{}, error)
|
||||
stripPassword func(string) string
|
||||
}
|
||||
|
||||
// parsers is a list of valid config parsers for the backends. The first parser
|
||||
// is the fallback and should always be set to the local backend.
|
||||
var parsers = []parser{
|
||||
{"b2", b2.ParseConfig},
|
||||
{"local", local.ParseConfig},
|
||||
{"sftp", sftp.ParseConfig},
|
||||
{"s3", s3.ParseConfig},
|
||||
{"gs", gs.ParseConfig},
|
||||
{"azure", azure.ParseConfig},
|
||||
{"swift", swift.ParseConfig},
|
||||
{"rest", rest.ParseConfig},
|
||||
{"rclone", rclone.ParseConfig},
|
||||
{"b2", b2.ParseConfig, noPassword},
|
||||
{"local", local.ParseConfig, noPassword},
|
||||
{"sftp", sftp.ParseConfig, noPassword},
|
||||
{"s3", s3.ParseConfig, noPassword},
|
||||
{"gs", gs.ParseConfig, noPassword},
|
||||
{"azure", azure.ParseConfig, noPassword},
|
||||
{"swift", swift.ParseConfig, noPassword},
|
||||
{"rest", rest.ParseConfig, rest.StripPassword},
|
||||
{"rclone", rclone.ParseConfig, noPassword},
|
||||
}
|
||||
|
||||
// noPassword returns the repository location unchanged (there's no sensitive information there)
|
||||
func noPassword(s string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
func isPath(s string) bool {
|
||||
@@ -107,6 +113,19 @@ func Parse(s string) (u Location, err error) {
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// StripPassword returns a displayable version of a repository location (with any sensitive information removed)
|
||||
func StripPassword(s string) string {
|
||||
scheme := extractScheme(s)
|
||||
|
||||
for _, parser := range parsers {
|
||||
if parser.scheme != scheme {
|
||||
continue
|
||||
}
|
||||
return parser.stripPassword(s)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func extractScheme(s string) string {
|
||||
data := strings.SplitN(s, ":", 2)
|
||||
return data[0]
|
||||
|
@@ -31,10 +31,7 @@ func ParseConfig(s string) (interface{}, error) {
|
||||
return nil, errors.New("invalid REST backend specification")
|
||||
}
|
||||
|
||||
s = s[5:]
|
||||
if !strings.HasSuffix(s, "/") {
|
||||
s += "/"
|
||||
}
|
||||
s = prepareURL(s)
|
||||
|
||||
u, err := url.Parse(s)
|
||||
|
||||
@@ -46,3 +43,31 @@ func ParseConfig(s string) (interface{}, error) {
|
||||
cfg.URL = u
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// StripPassword removes the password from the URL
|
||||
// If the repository location cannot be parsed as a valid URL, it will be returned as is
|
||||
// (it's because this function is used for logging errors)
|
||||
func StripPassword(s string) string {
|
||||
scheme := s[:5]
|
||||
s = prepareURL(s)
|
||||
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return scheme + s
|
||||
}
|
||||
|
||||
if _, set := u.User.Password(); !set {
|
||||
return scheme + s
|
||||
}
|
||||
|
||||
// a password was set: we replace it with ***
|
||||
return scheme + strings.Replace(u.String(), u.User.String()+"@", u.User.Username()+":***@", 1)
|
||||
}
|
||||
|
||||
func prepareURL(s string) string {
|
||||
s = s[5:]
|
||||
if !strings.HasSuffix(s, "/") {
|
||||
s += "/"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
Reference in New Issue
Block a user