security: sanitize token from re-exec log line
os.Args logged during self-update re-exec could expose --token value in plaintext to log aggregators. sanitizeArgs masks the value.
This commit is contained in:
parent
a0516c2bd1
commit
442b019b0b
2 changed files with 55 additions and 1 deletions
17
update.go
17
update.go
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -78,6 +79,20 @@ func selfUpdate(downloadURL, expectedChecksum string) error {
|
||||||
slog.Info("binary replaced", "path", currentExe)
|
slog.Info("binary replaced", "path", currentExe)
|
||||||
|
|
||||||
// Re-exec with same arguments
|
// Re-exec with same arguments
|
||||||
slog.Info("re-executing", "args", os.Args)
|
slog.Info("re-executing", "args", sanitizeArgs(os.Args))
|
||||||
return syscall.Exec(currentExe, os.Args, os.Environ())
|
return syscall.Exec(currentExe, os.Args, os.Environ())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sanitizeArgs returns a copy of args with token values masked.
|
||||||
|
func sanitizeArgs(args []string) []string {
|
||||||
|
out := make([]string, len(args))
|
||||||
|
copy(out, args)
|
||||||
|
for i, a := range out {
|
||||||
|
if (a == "--token" || a == "-token") && i+1 < len(out) {
|
||||||
|
out[i+1] = "***"
|
||||||
|
} else if strings.HasPrefix(a, "--token=") || strings.HasPrefix(a, "-token=") {
|
||||||
|
out[i] = a[:strings.Index(a, "=")+1] + "***"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -283,6 +283,45 @@ func TestSelfUpdateTooLarge(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSanitizeArgs(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
in []string
|
||||||
|
want []string
|
||||||
|
}{
|
||||||
|
{"flag with separate value", []string{"agent", "--token", "secret"}, []string{"agent", "--token", "***"}},
|
||||||
|
{"no sensitive flags", []string{"agent", "--api-url", "wss://x"}, []string{"agent", "--api-url", "wss://x"}},
|
||||||
|
{"equals syntax", []string{"agent", "--token=secret"}, []string{"agent", "--token=***"}},
|
||||||
|
{"empty args", []string{"agent"}, []string{"agent"}},
|
||||||
|
{"trailing flag no value", []string{"agent", "--token"}, []string{"agent", "--token"}},
|
||||||
|
{"short flag with value", []string{"agent", "-token", "secret"}, []string{"agent", "-token", "***"}},
|
||||||
|
{"short equals syntax", []string{"agent", "-token=secret"}, []string{"agent", "-token=***"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := sanitizeArgs(tt.in)
|
||||||
|
if len(got) != len(tt.want) {
|
||||||
|
t.Fatalf("length: got %d, want %d", len(got), len(tt.want))
|
||||||
|
}
|
||||||
|
for i := range got {
|
||||||
|
if got[i] != tt.want[i] {
|
||||||
|
t.Errorf("arg[%d]: got %q, want %q", i, got[i], tt.want[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify original slice is not mutated
|
||||||
|
t.Run("does not mutate input", func(t *testing.T) {
|
||||||
|
orig := []string{"agent", "--token", "secret"}
|
||||||
|
_ = sanitizeArgs(orig)
|
||||||
|
if orig[2] != "secret" {
|
||||||
|
t.Error("sanitizeArgs mutated the input slice")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// rewriteToHTTPS converts an httptest TLS server URL to use the https scheme.
|
// rewriteToHTTPS converts an httptest TLS server URL to use the https scheme.
|
||||||
// httptest.NewTLSServer returns URLs with https:// already, but this ensures consistency.
|
// httptest.NewTLSServer returns URLs with https:// already, but this ensures consistency.
|
||||||
func rewriteToHTTPS(rawURL string) string {
|
func rewriteToHTTPS(rawURL string) string {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue