// Package output removes credentials from diagnostics before they reach an operator terminal. package output import ( "errors" "io" "os" "regexp" "sort" "strings" ) 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) { info, err := os.Lstat(path) if err != nil || !info.Mode().IsRegular() || info.Size() > maxSecretFileBytes { return "", errors.New("declared secret file could not be read") } file, err := os.Open(path) if err != nil { return "", errors.New("declared secret file could not be read") } defer file.Close() contents, err := io.ReadAll(io.LimitReader(file, maxSecretFileBytes+1)) if err != nil || len(contents) > maxSecretFileBytes { return "", errors.New("declared secret file could not be read") } return strings.TrimRight(string(contents), "\r\n"), nil }