57 lines
1.6 KiB
Go
57 lines
1.6 KiB
Go
// Package output removes credentials from diagnostics before they reach an operator terminal.
|
|
package output
|
|
|
|
import (
|
|
"errors"
|
|
"regexp"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/aritmolab/thothii/tools/thothctl/internal/safeio"
|
|
)
|
|
|
|
var credentialField = regexp.MustCompile(`(?im)(\b[\w.-]*(?:password|token|key)[\w.-]*\s*[:=]\s*)(?:"[^"\r\n]*"|'[^'\r\n]*'|[^\s,;]+)`)
|
|
|
|
const maxSecretFileBytes = 64 * 1024
|
|
|
|
// 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))
|
|
seen := make(map[string]struct{})
|
|
for _, path := range paths {
|
|
value, err := readSecretFile(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if value != "" {
|
|
if _, exists := seen[value]; exists {
|
|
continue
|
|
}
|
|
values = append(values, value)
|
|
seen[value] = struct{}{}
|
|
}
|
|
}
|
|
return values, nil
|
|
}
|
|
|
|
func readSecretFile(path string) (string, error) {
|
|
contents, err := safeio.ReadCanonicalRegular(path, maxSecretFileBytes)
|
|
if err != nil {
|
|
return "", errors.New("declared secret file could not be read")
|
|
}
|
|
return strings.TrimRight(string(contents), "\r\n"), nil
|
|
}
|