fix: harden thothctl diagnostics
This commit is contained in:
@@ -2,7 +2,8 @@
|
||||
package output
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
@@ -11,6 +12,8 @@ import (
|
||||
|
||||
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]")
|
||||
@@ -27,14 +30,36 @@ func Sanitize(text string, secretValues []string) string {
|
||||
// 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 {
|
||||
contents, err := os.ReadFile(path)
|
||||
value, err := readSecretFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read secret file: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
if value := strings.TrimSpace(string(contents)); value != "" {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -33,3 +33,16 @@ func TestSanitizeRedactsSecretFileContents(t *testing.T) {
|
||||
t.Errorf("Sanitize() = %q, want redacted secret", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecretValuesFromFilesRejectsOversizedFiles(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
secretFile := filepath.Join(t.TempDir(), "oversized-token")
|
||||
if err := os.WriteFile(secretFile, make([]byte, 64*1024+1), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := SecretValuesFromFiles([]string{secretFile}); err == nil {
|
||||
t.Fatal("SecretValuesFromFiles() error = nil, want oversized-file error")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user