fix: harden thothctl diagnostics

This commit is contained in:
2026-08-04 17:00:05 +02:00
parent 853a151796
commit 4158990c10
7 changed files with 351 additions and 12 deletions
@@ -8,12 +8,15 @@ import (
"io"
"os"
"path/filepath"
"strings"
"gopkg.in/yaml.v3"
)
const installationFileName = "thothii-installation.yaml"
const maxEnvironmentFileBytes = 1 << 20
type descriptor struct {
Profile string `yaml:"profile"`
ProjectDirectory string `yaml:"projectDirectory"`
@@ -116,6 +119,62 @@ func (i Installation) ComposeArgs(command ...string) []string {
return append(args, command...)
}
// SecretFiles returns only existing, absolute regular files declared in the installation env file
// through *_FILE or *_SOURCE variables. Missing paths are allowed because /run/secrets paths are
// container-local declarations, not host files thothctl can read.
func (i Installation) SecretFiles() ([]string, error) {
info, err := os.Stat(i.EnvFile)
if err != nil || info.Size() > maxEnvironmentFileBytes {
return nil, errors.New("installation secret declarations could not be read")
}
contents, err := os.ReadFile(i.EnvFile)
if err != nil || len(contents) > maxEnvironmentFileBytes {
return nil, errors.New("installation secret declarations could not be read")
}
files := make([]string, 0)
seen := make(map[string]struct{})
for _, line := range strings.Split(string(contents), "\n") {
key, value, ok := environmentAssignment(line)
if !ok || (!strings.HasSuffix(key, "_FILE") && !strings.HasSuffix(key, "_SOURCE")) || !filepath.IsAbs(value) {
continue
}
fileInfo, err := os.Lstat(value)
if errors.Is(err, os.ErrNotExist) {
continue
}
if err != nil || !fileInfo.Mode().IsRegular() {
return nil, errors.New("installation secret declarations could not be read")
}
if _, exists := seen[value]; !exists {
files = append(files, value)
seen[value] = struct{}{}
}
}
return files, nil
}
func environmentAssignment(line string) (string, string, bool) {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
return "", "", false
}
line = strings.TrimPrefix(line, "export ")
key, value, found := strings.Cut(line, "=")
if !found {
return "", "", false
}
key = strings.TrimSpace(key)
if key == "" {
return "", "", false
}
value = strings.TrimSpace(value)
if len(value) >= 2 && ((value[0] == '"' && value[len(value)-1] == '"') || (value[0] == '\'' && value[len(value)-1] == '\'')) {
value = value[1 : len(value)-1]
}
return strings.ToUpper(key), value, true
}
func ensureOnlyOneDocument(decoder *yaml.Decoder) error {
var extra any
err := decoder.Decode(&extra)