fix(summer): mask secret flag values in a delegate's error (WR-10)

This commit is contained in:
Jakub Zych
2026-10-22 04:00:31 +02:00
parent 707372bf65
commit 17c722da46
7 changed files with 147 additions and 65 deletions

View File

@@ -0,0 +1,63 @@
// Package argmask renders a command line for messages with the values of
// secret-named long flags masked; bonfire's wrong-directory hint and
// summer's delegate errors share it.
package argmask
import "strings"
// secretWords mark a long flag whose value is masked.
var secretWords = []string{"password", "passwd", "secret", "token", "key"}
// secretFlag reports whether the long flag name (without "--") looks like it
// carries a secret.
func secretFlag(name string) bool {
name = strings.ToLower(name)
for _, w := range secretWords {
if strings.Contains(name, w) {
return true
}
}
return false
}
// Join renders args as one line, separated by single spaces and each
// quoted for a shell (Quote). The value of a long flag named like a secret
// becomes ***, in the "--flag=value" form and in the "--flag value" form,
// so a password typed on the command line does not reach stderr and CI
// logs. In the "--flag value" form the next argument is masked whatever its
// first character (pflag takes "-Xk9" as the value too); only "--" is left
// alone. Join cannot see flag types, so a secret-named boolean flag
// over-masks the argument after it, which is the safe side.
func Join(args []string) string {
var b strings.Builder
for i := 0; i < len(args); i++ {
if i > 0 {
b.WriteByte(' ')
}
a := args[i]
name, long := strings.CutPrefix(a, "--")
flag, _, inline := strings.Cut(name, "=")
if !long || flag == "" || !secretFlag(flag) {
b.WriteString(Quote(a))
continue
}
if inline {
b.WriteString(Quote("--"+flag) + "=***")
continue
}
b.WriteString(Quote(a))
if i+1 < len(args) && args[i+1] != "--" {
b.WriteString(" ***")
i++
}
}
return b.String()
}
// Quote single-quotes s when a shell would split or expand it.
func Quote(s string) string {
if s != "" && !strings.ContainsAny(s, " \t\n'\"\\$`*?[]{}()<>|&;#~!") {
return s
}
return "'" + strings.ReplaceAll(s, "'", `'\''`) + "'"
}