From 05431e71e175d005a89d81f25cf5bb5d8347ee95 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 26 Mar 2022 20:29:30 +0800 Subject: [PATCH 1/8] Hide sensitive content on admin panel progress monitor --- modules/git/command.go | 16 +++++++++++++++- modules/git/repo.go | 7 +++++-- services/mirror/mirror_pull.go | 8 ++++++-- services/mirror/mirror_push.go | 4 +++- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/modules/git/command.go b/modules/git/command.go index 649d9cf249f39..dab6f6423f575 100644 --- a/modules/git/command.go +++ b/modules/git/command.go @@ -17,6 +17,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/process" + "code.gitea.io/gitea/modules/util" ) var ( @@ -34,6 +35,7 @@ const DefaultLocale = "C" type Command struct { name string args []string + urlArgInts []int parentContext context.Context desc string } @@ -90,6 +92,13 @@ func (c *Command) AddArguments(args ...string) *Command { return c } +// AddURLArgument adds an argument which is a url which means password should be shadow when display in UI +func (c *Command) AddURLArgument(arg string) *Command { + c.urlArgInts = append(c.urlArgInts, len(c.args)) + c.args = append(c.args, arg) + return c +} + // RunInDirTimeoutEnvPipeline executes the command in given directory with given timeout, // it pipes stdout and stderr to given io.Writer. func (c *Command) RunInDirTimeoutEnvPipeline(env []string, timeout time.Duration, dir string, stdout, stderr io.Writer) error { @@ -140,7 +149,12 @@ func (c *Command) RunWithContext(rc *RunContext) error { desc := c.desc if desc == "" { - desc = fmt.Sprintf("%s %s [repo_path: %s]", c.name, strings.Join(c.args, " "), rc.Dir) + args := make([]string, len(c.args)) + copy(args, c.args) + for _, i := range c.urlArgInts { + args[i] = util.NewStringURLSanitizer(args[i], true).Replace(args[i]) + } + desc = fmt.Sprintf("%s %s [repo_path: %s]", c.name, strings.Join(args, " "), rc.Dir) } ctx, cancel, finished := process.GetManager().AddContextTimeout(c.parentContext, rc.Timeout, desc) diff --git a/modules/git/repo.go b/modules/git/repo.go index 5ba39ac7e3bbc..d245f816c1bb2 100644 --- a/modules/git/repo.go +++ b/modules/git/repo.go @@ -152,7 +152,9 @@ func CloneWithArgs(ctx context.Context, from, to string, args []string, opts Clo if len(opts.Branch) > 0 { cmd.AddArguments("-b", opts.Branch) } - cmd.AddArguments("--", from, to) + cmd.AddArguments("--") + cmd.AddURLArgument(from) + cmd.AddArguments(to) if opts.Timeout <= 0 { opts.Timeout = -1 @@ -197,7 +199,8 @@ func Push(ctx context.Context, repoPath string, opts PushOptions) error { if opts.Mirror { cmd.AddArguments("--mirror") } - cmd.AddArguments("--", opts.Remote) + cmd.AddArguments("--") + cmd.AddURLArgument(opts.Remote) if len(opts.Branch) > 0 { cmd.AddArguments(opts.Branch) } diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index 979ef7f03cf1a..e2b49217c7188 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -38,7 +38,9 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error return err } - _, err = git.NewCommand(ctx, "remote", "add", remoteName, "--mirror=fetch", addr).RunInDir(repoPath) + cmd := git.NewCommand(ctx, "remote", "add", remoteName, "--mirror=fetch") + cmd.AddURLArgument(addr) + _, err = cmd.RunInDir(repoPath) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { return err } @@ -52,7 +54,9 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error return err } - _, err = git.NewCommand(ctx, "remote", "add", remoteName, "--mirror=fetch", wikiRemotePath).RunInDir(wikiPath) + cmd = git.NewCommand(ctx, "remote", "add", remoteName, "--mirror=fetch") + cmd.AddURLArgument(wikiRemotePath) + _, err = cmd.RunInDir(wikiPath) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { return err } diff --git a/services/mirror/mirror_push.go b/services/mirror/mirror_push.go index cff53ba8d068f..b1e7b8ad3be31 100644 --- a/services/mirror/mirror_push.go +++ b/services/mirror/mirror_push.go @@ -28,7 +28,9 @@ var stripExitStatus = regexp.MustCompile(`exit status \d+ - `) // AddPushMirrorRemote registers the push mirror remote. func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr string) error { addRemoteAndConfig := func(addr, path string) error { - if _, err := git.NewCommand(ctx, "remote", "add", "--mirror=push", m.RemoteName, addr).RunInDir(path); err != nil { + cmd := git.NewCommand(ctx, "remote", "add", "--mirror=push", m.RemoteName) + cmd.AddURLArgument(addr) + if _, err := cmd.RunInDir(path); err != nil { return err } if _, err := git.NewCommand(ctx, "config", "--add", "remote."+m.RemoteName+".push", "+refs/heads/*:refs/heads/*").RunInDir(path); err != nil { From 230b6772285d46b9d300a90a723683b626450a71 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 26 Mar 2022 22:14:07 +0800 Subject: [PATCH 2/8] Improve variable names --- modules/git/command.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/git/command.go b/modules/git/command.go index dab6f6423f575..597b9de1fa06a 100644 --- a/modules/git/command.go +++ b/modules/git/command.go @@ -35,7 +35,7 @@ const DefaultLocale = "C" type Command struct { name string args []string - urlArgInts []int + urlArgIndexes []int parentContext context.Context desc string } @@ -94,7 +94,7 @@ func (c *Command) AddArguments(args ...string) *Command { // AddURLArgument adds an argument which is a url which means password should be shadow when display in UI func (c *Command) AddURLArgument(arg string) *Command { - c.urlArgInts = append(c.urlArgInts, len(c.args)) + c.urlArgIndexes = append(c.urlArgIndexes, len(c.args)) c.args = append(c.args, arg) return c } @@ -151,8 +151,8 @@ func (c *Command) RunWithContext(rc *RunContext) error { if desc == "" { args := make([]string, len(c.args)) copy(args, c.args) - for _, i := range c.urlArgInts { - args[i] = util.NewStringURLSanitizer(args[i], true).Replace(args[i]) + for _, urlArgIndex := range c.urlArgIndexes { + args[urlArgIndex] = util.NewStringURLSanitizer(args[urlArgIndex], true).Replace(args[urlArgIndex]) } desc = fmt.Sprintf("%s %s [repo_path: %s]", c.name, strings.Join(args, " "), rc.Dir) } From d17eb331af57c34724ab08b803c65783cd09362e Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 27 Mar 2022 00:18:53 +0800 Subject: [PATCH 3/8] performance optimization --- modules/git/command.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/modules/git/command.go b/modules/git/command.go index 597b9de1fa06a..fb375e4b6bb86 100644 --- a/modules/git/command.go +++ b/modules/git/command.go @@ -149,10 +149,15 @@ func (c *Command) RunWithContext(rc *RunContext) error { desc := c.desc if desc == "" { - args := make([]string, len(c.args)) - copy(args, c.args) - for _, urlArgIndex := range c.urlArgIndexes { - args[urlArgIndex] = util.NewStringURLSanitizer(args[urlArgIndex], true).Replace(args[urlArgIndex]) + var args []string + if len(c.urlArgIndexes) == 0 { + args = c.args + } else { + args = make([]string, len(c.args)) + copy(args, c.args) + for _, urlArgIndex := range c.urlArgIndexes { + args[urlArgIndex] = util.NewStringURLSanitizer(args[urlArgIndex], true).Replace(args[urlArgIndex]) + } } desc = fmt.Sprintf("%s %s [repo_path: %s]", c.name, strings.Join(args, " "), rc.Dir) } From 2dff616cdf11f0cb04fb2d86b8c308d74091a621 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 27 Mar 2022 15:40:55 +0800 Subject: [PATCH 4/8] Add description for the three commands and also replace possible sensitive content when generating description automatically --- modules/git/command.go | 21 +++++++++------------ modules/git/repo.go | 11 ++++++----- services/mirror/mirror_pull.go | 8 ++++---- services/mirror/mirror_push.go | 4 ++-- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/modules/git/command.go b/modules/git/command.go index fb375e4b6bb86..9b743241fa40b 100644 --- a/modules/git/command.go +++ b/modules/git/command.go @@ -92,13 +92,6 @@ func (c *Command) AddArguments(args ...string) *Command { return c } -// AddURLArgument adds an argument which is a url which means password should be shadow when display in UI -func (c *Command) AddURLArgument(arg string) *Command { - c.urlArgIndexes = append(c.urlArgIndexes, len(c.args)) - c.args = append(c.args, arg) - return c -} - // RunInDirTimeoutEnvPipeline executes the command in given directory with given timeout, // it pipes stdout and stderr to given io.Writer. func (c *Command) RunInDirTimeoutEnvPipeline(env []string, timeout time.Duration, dir string, stdout, stderr io.Writer) error { @@ -149,13 +142,17 @@ func (c *Command) RunWithContext(rc *RunContext) error { desc := c.desc if desc == "" { - var args []string - if len(c.urlArgIndexes) == 0 { - args = c.args - } else { + args := c.args + var argSensitiveURLIndexes []int + for i, arg := range c.args { + if strings.Index(arg, "://") != -1 && strings.IndexByte(arg, '@') != -1 { + argSensitiveURLIndexes = append(argSensitiveURLIndexes, i) + } + } + if len(argSensitiveURLIndexes) > 0 { args = make([]string, len(c.args)) copy(args, c.args) - for _, urlArgIndex := range c.urlArgIndexes { + for _, urlArgIndex := range argSensitiveURLIndexes { args[urlArgIndex] = util.NewStringURLSanitizer(args[urlArgIndex], true).Replace(args[urlArgIndex]) } } diff --git a/modules/git/repo.go b/modules/git/repo.go index d245f816c1bb2..d627e86f9e2d8 100644 --- a/modules/git/repo.go +++ b/modules/git/repo.go @@ -19,6 +19,7 @@ import ( "time" "code.gitea.io/gitea/modules/proxy" + "code.gitea.io/gitea/modules/util" ) // GPGSettings represents the default GPG settings for this repository @@ -152,9 +153,8 @@ func CloneWithArgs(ctx context.Context, from, to string, args []string, opts Clo if len(opts.Branch) > 0 { cmd.AddArguments("-b", opts.Branch) } - cmd.AddArguments("--") - cmd.AddURLArgument(from) - cmd.AddArguments(to) + cmd.AddArguments("--", from, to). + SetDescription(fmt.Sprintf("clone from %s to %s", util.NewStringURLSanitizer(from, true).Replace(from), to)) if opts.Timeout <= 0 { opts.Timeout = -1 @@ -199,11 +199,12 @@ func Push(ctx context.Context, repoPath string, opts PushOptions) error { if opts.Mirror { cmd.AddArguments("--mirror") } - cmd.AddArguments("--") - cmd.AddURLArgument(opts.Remote) + cmd.AddArguments("--", opts.Remote) if len(opts.Branch) > 0 { cmd.AddArguments(opts.Branch) } + cmd.SetDescription(fmt.Sprintf("push %s to %s", repoPath, util.NewStringURLSanitizer(opts.Remote, true).Replace(opts.Remote))) + var outbuf, errbuf strings.Builder if opts.Timeout == 0 { diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index e2b49217c7188..d5749cb0a71bc 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -38,8 +38,8 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error return err } - cmd := git.NewCommand(ctx, "remote", "add", remoteName, "--mirror=fetch") - cmd.AddURLArgument(addr) + cmd := git.NewCommand(ctx, "remote", "add", remoteName, "--mirror=fetch", addr) + cmd.SetDescription(fmt.Sprintf("add fetch remote %s to %s", util.NewStringURLSanitizer(addr, true).Replace(addr), repoPath)) _, err = cmd.RunInDir(repoPath) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { return err @@ -54,8 +54,8 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error return err } - cmd = git.NewCommand(ctx, "remote", "add", remoteName, "--mirror=fetch") - cmd.AddURLArgument(wikiRemotePath) + cmd = git.NewCommand(ctx, "remote", "add", remoteName, "--mirror=fetch", wikiRemotePath) + cmd.SetDescription(fmt.Sprintf("add fetch remote %s to %s", util.NewStringURLSanitizer(wikiRemotePath, true).Replace(wikiRemotePath), wikiPath)) _, err = cmd.RunInDir(wikiPath) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { return err diff --git a/services/mirror/mirror_push.go b/services/mirror/mirror_push.go index b1e7b8ad3be31..64165da1d8511 100644 --- a/services/mirror/mirror_push.go +++ b/services/mirror/mirror_push.go @@ -28,8 +28,8 @@ var stripExitStatus = regexp.MustCompile(`exit status \d+ - `) // AddPushMirrorRemote registers the push mirror remote. func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr string) error { addRemoteAndConfig := func(addr, path string) error { - cmd := git.NewCommand(ctx, "remote", "add", "--mirror=push", m.RemoteName) - cmd.AddURLArgument(addr) + cmd := git.NewCommand(ctx, "remote", "add", "--mirror=push", m.RemoteName, addr) + cmd.SetDescription(fmt.Sprintf("add push remote %s to %s", util.NewStringURLSanitizer(addr, true).Replace(addr), path)) if _, err := cmd.RunInDir(path); err != nil { return err } From 4177033e2ce181905e6dbdbac17952d36012464e Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 27 Mar 2022 15:44:24 +0800 Subject: [PATCH 5/8] Remove unused urlArgIndexes --- modules/git/command.go | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/git/command.go b/modules/git/command.go index 9b743241fa40b..0594f703b8d02 100644 --- a/modules/git/command.go +++ b/modules/git/command.go @@ -35,7 +35,6 @@ const DefaultLocale = "C" type Command struct { name string args []string - urlArgIndexes []int parentContext context.Context desc string } From fe57d61c605076a4c62f920c807a67961acc4668 Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Sun, 27 Mar 2022 10:14:40 +0100 Subject: [PATCH 6/8] fix lint Signed-off-by: Andrew Thornton --- modules/git/command.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/git/command.go b/modules/git/command.go index a72c36f52ebf6..8199498a2b2b8 100644 --- a/modules/git/command.go +++ b/modules/git/command.go @@ -146,7 +146,7 @@ func (c *Command) RunWithContext(rc *RunContext) error { args := c.args[c.globalArgsLength:] var argSensitiveURLIndexes []int for i, arg := range c.args { - if strings.Index(arg, "://") != -1 && strings.IndexByte(arg, '@') != -1 { + if strings.Contains(arg, "://") && strings.Contains(arg, "@") { argSensitiveURLIndexes = append(argSensitiveURLIndexes, i) } } From 941542458efdd24c21bd1a67aec671fa847adc76 Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Sun, 27 Mar 2022 12:00:19 +0100 Subject: [PATCH 7/8] Adjust descriptions Signed-off-by: Andrew Thornton --- modules/git/repo.go | 15 +++++++++++++-- services/mirror/mirror_pull.go | 12 ++++++++++-- services/mirror/mirror_push.go | 7 +++++++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/modules/git/repo.go b/modules/git/repo.go index d627e86f9e2d8..39544e0ec0ac7 100644 --- a/modules/git/repo.go +++ b/modules/git/repo.go @@ -153,8 +153,13 @@ func CloneWithArgs(ctx context.Context, from, to string, args []string, opts Clo if len(opts.Branch) > 0 { cmd.AddArguments("-b", opts.Branch) } - cmd.AddArguments("--", from, to). - SetDescription(fmt.Sprintf("clone from %s to %s", util.NewStringURLSanitizer(from, true).Replace(from), to)) + cmd.AddArguments("--", from, to) + + if strings.Contains(from, "://") && strings.Contains(from, "@") { + cmd.SetDescription(fmt.Sprintf("clone branch %s from %s to %s (shared: %t, mirror: %t, depth: %d)", opts.Branch, util.NewStringURLSanitizer(from, true).Replace(from), to, opts.Shared, opts.Mirror, opts.Depth)) + } else { + cmd.SetDescription(fmt.Sprintf("clone branch %s from %s to %s (shared: %t, mirror: %t, depth: %d)", opts.Branch, from, to, opts.Shared, opts.Mirror, opts.Depth)) + } if opts.Timeout <= 0 { opts.Timeout = -1 @@ -203,6 +208,12 @@ func Push(ctx context.Context, repoPath string, opts PushOptions) error { if len(opts.Branch) > 0 { cmd.AddArguments(opts.Branch) } + if strings.Contains(opts.Remote, "://") && strings.Contains(opts.Remote, "@") { + cmd.SetDescription(fmt.Sprintf("push branch %s to %s (force: %t, mirror: %t)", opts.Branch, util.NewStringURLSanitizer(opts.Remote, true).Replace(opts.Remote), opts.Force, opts.Mirror)) + } else { + cmd.SetDescription(fmt.Sprintf("push branch %s to %s (force: %t, mirror: %t)", opts.Branch, opts.Remote, opts.Force, opts.Mirror)) + } + cmd.SetDescription(fmt.Sprintf("push %s to %s", repoPath, util.NewStringURLSanitizer(opts.Remote, true).Replace(opts.Remote))) var outbuf, errbuf strings.Builder diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index d5749cb0a71bc..d032a932cf52b 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -39,7 +39,11 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error } cmd := git.NewCommand(ctx, "remote", "add", remoteName, "--mirror=fetch", addr) - cmd.SetDescription(fmt.Sprintf("add fetch remote %s to %s", util.NewStringURLSanitizer(addr, true).Replace(addr), repoPath)) + if strings.Contains(addr, "://") && strings.Contains(addr, "@") { + cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, util.NewStringURLSanitizer(addr, true).Replace(addr), repoPath)) + } else { + cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, addr, repoPath)) + } _, err = cmd.RunInDir(repoPath) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { return err @@ -55,7 +59,11 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error } cmd = git.NewCommand(ctx, "remote", "add", remoteName, "--mirror=fetch", wikiRemotePath) - cmd.SetDescription(fmt.Sprintf("add fetch remote %s to %s", util.NewStringURLSanitizer(wikiRemotePath, true).Replace(wikiRemotePath), wikiPath)) + if strings.Contains(wikiRemotePath, "://") && strings.Contains(wikiRemotePath, "@") { + cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, util.NewStringURLSanitizer(wikiRemotePath, true).Replace(wikiRemotePath), wikiPath)) + } else { + cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, wikiRemotePath, wikiPath)) + } _, err = cmd.RunInDir(wikiPath) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { return err diff --git a/services/mirror/mirror_push.go b/services/mirror/mirror_push.go index 64165da1d8511..0685bb03a54d6 100644 --- a/services/mirror/mirror_push.go +++ b/services/mirror/mirror_push.go @@ -10,6 +10,7 @@ import ( "fmt" "io" "regexp" + "strings" "time" repo_model "code.gitea.io/gitea/models/repo" @@ -29,6 +30,12 @@ var stripExitStatus = regexp.MustCompile(`exit status \d+ - `) func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr string) error { addRemoteAndConfig := func(addr, path string) error { cmd := git.NewCommand(ctx, "remote", "add", "--mirror=push", m.RemoteName, addr) + if strings.Contains(addr, "://") && strings.Contains(addr, "@") { + cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=push %s [repo_path: %s]", m.RemoteName, util.NewStringURLSanitizer(addr, true).Replace(addr), path)) + } else { + cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=push %s [repo_path: %s]", m.RemoteName, addr, path)) + } + cmd.SetDescription(fmt.Sprintf("add push remote %s to %s", util.NewStringURLSanitizer(addr, true).Replace(addr), path)) if _, err := cmd.RunInDir(path); err != nil { return err From d27faad37c4d5659fb61abbe5da126cef7c14201 Mon Sep 17 00:00:00 2001 From: zeripath Date: Sun, 27 Mar 2022 12:03:10 +0100 Subject: [PATCH 8/8] remove extraneous description setting --- modules/git/repo.go | 3 --- services/mirror/mirror_push.go | 2 -- 2 files changed, 5 deletions(-) diff --git a/modules/git/repo.go b/modules/git/repo.go index 39544e0ec0ac7..b886d5ed45a86 100644 --- a/modules/git/repo.go +++ b/modules/git/repo.go @@ -213,9 +213,6 @@ func Push(ctx context.Context, repoPath string, opts PushOptions) error { } else { cmd.SetDescription(fmt.Sprintf("push branch %s to %s (force: %t, mirror: %t)", opts.Branch, opts.Remote, opts.Force, opts.Mirror)) } - - cmd.SetDescription(fmt.Sprintf("push %s to %s", repoPath, util.NewStringURLSanitizer(opts.Remote, true).Replace(opts.Remote))) - var outbuf, errbuf strings.Builder if opts.Timeout == 0 { diff --git a/services/mirror/mirror_push.go b/services/mirror/mirror_push.go index 0685bb03a54d6..b619f9ab32344 100644 --- a/services/mirror/mirror_push.go +++ b/services/mirror/mirror_push.go @@ -35,8 +35,6 @@ func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr str } else { cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=push %s [repo_path: %s]", m.RemoteName, addr, path)) } - - cmd.SetDescription(fmt.Sprintf("add push remote %s to %s", util.NewStringURLSanitizer(addr, true).Replace(addr), path)) if _, err := cmd.RunInDir(path); err != nil { return err }