feat: add cross-platform thothctl
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
// Package output removes credentials from diagnostics before they reach an operator terminal.
|
||||
package output
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var credentialField = regexp.MustCompile(`(?im)(\b[\w.-]*(?:password|token|key)[\w.-]*\s*[:=]\s*)(?:"[^"\r\n]*"|'[^'\r\n]*'|[^\s,;]+)`)
|
||||
|
||||
// Sanitize redacts common credential fields and every supplied secret value.
|
||||
func Sanitize(text string, secretValues []string) string {
|
||||
text = credentialField.ReplaceAllString(text, "${1}[REDACTED]")
|
||||
values := append([]string(nil), secretValues...)
|
||||
sort.Slice(values, func(i, j int) bool { return len(values[i]) > len(values[j]) })
|
||||
for _, value := range values {
|
||||
if value != "" {
|
||||
text = strings.ReplaceAll(text, value, "[REDACTED]")
|
||||
}
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
// SecretValuesFromFiles reads non-empty secret-file contents without exposing them to callers.
|
||||
func SecretValuesFromFiles(paths []string) ([]string, error) {
|
||||
values := make([]string, 0, len(paths))
|
||||
for _, path := range paths {
|
||||
contents, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read secret file: %w", err)
|
||||
}
|
||||
if value := strings.TrimSpace(string(contents)); value != "" {
|
||||
values = append(values, value)
|
||||
}
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package output
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSanitizeRedactsPasswordTokenAndKeyFields(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got := Sanitize("DB_PASSWORD=hunter2\naccess_token: abc123\napi-key = quoted-value\nplain=safe\n", nil)
|
||||
want := "DB_PASSWORD=[REDACTED]\naccess_token: [REDACTED]\napi-key = [REDACTED]\nplain=safe\n"
|
||||
if got != want {
|
||||
t.Errorf("Sanitize() = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSanitizeRedactsSecretFileContents(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
secretFile := filepath.Join(t.TempDir(), "provider-token")
|
||||
if err := os.WriteFile(secretFile, []byte("top-secret-value\n"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
secrets, err := SecretValuesFromFiles([]string{secretFile})
|
||||
if err != nil {
|
||||
t.Fatalf("SecretValuesFromFiles() error = %v", err)
|
||||
}
|
||||
got := Sanitize("request failed for top-secret-value", secrets)
|
||||
if got != "request failed for [REDACTED]" {
|
||||
t.Errorf("Sanitize() = %q, want redacted secret", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user