Summary
The SOPS secrets provider supports unencrypted secrets files (secrets.yaml, secrets.yml) alongside the encrypted variants, loads them silently, and the .gitignore that Windsor generates for a context does not exclude them. A user who drops a plaintext secrets.yaml will have it tracked (and pushed) by git by default.
This is the highest-leverage secrets-handling gap: it turns an easy mistake into a committed-plaintext-secret.
Evidence
findSecretsFilePaths() globs for both plaintext and encrypted files and loads any that exist:
pkg/runtime/secrets/sops_provider.go:16-21 — filename constants include the plaintext secrets.yaml / secrets.yml.
pkg/runtime/secrets/sops_provider.go:169-184 — candidates include the plaintext names; LoadSecrets (:93) runs sops --decrypt on every match.
Secrets files live at contexts/<ctx>/secrets.yaml (NewSopsProvider(rt.ConfigRoot) at pkg/runtime/runtime.go:844, ConfigRoot = contexts/<ctx>).
The only per-context .gitignore Windsor writes omits the secrets files:
// pkg/composer/composer.go:190
const contextIgnoreContent = ".kube/\n.talos/\n.omni/\n.aws/\n.azure/\n.gcp/\n.env\n"
Impact
Accidental commit of plaintext secrets to a shared repo — the worst-case leakage boundary for a dev tool.
Suggested fix
- Add
secrets.yaml and secrets.yml to contextIgnoreContent in pkg/composer/composer.go:190. (The encrypted .enc.yaml variant is safe to commit and should stay tracked.)
- Additionally, reconsider silently loading an unencrypted secrets file:
sops --decrypt on a plaintext file is a nonsensical path. Prefer refusing to load it with a loud warning, so a raw secrets.yaml is a hard error rather than a silent success.
Both changes are small and self-contained.
Summary
The SOPS secrets provider supports unencrypted secrets files (
secrets.yaml,secrets.yml) alongside the encrypted variants, loads them silently, and the.gitignorethat Windsor generates for a context does not exclude them. A user who drops a plaintextsecrets.yamlwill have it tracked (and pushed) by git by default.This is the highest-leverage secrets-handling gap: it turns an easy mistake into a committed-plaintext-secret.
Evidence
findSecretsFilePaths()globs for both plaintext and encrypted files and loads any that exist:pkg/runtime/secrets/sops_provider.go:16-21— filename constants include the plaintextsecrets.yaml/secrets.yml.pkg/runtime/secrets/sops_provider.go:169-184— candidates include the plaintext names;LoadSecrets(:93) runssops --decrypton every match.Secrets files live at
contexts/<ctx>/secrets.yaml(NewSopsProvider(rt.ConfigRoot)atpkg/runtime/runtime.go:844,ConfigRoot = contexts/<ctx>).The only per-context
.gitignoreWindsor writes omits the secrets files:Impact
Accidental commit of plaintext secrets to a shared repo — the worst-case leakage boundary for a dev tool.
Suggested fix
secrets.yamlandsecrets.ymltocontextIgnoreContentinpkg/composer/composer.go:190. (The encrypted.enc.yamlvariant is safe to commit and should stay tracked.)sops --decrypton a plaintext file is a nonsensical path. Prefer refusing to load it with a loud warning, so a rawsecrets.yamlis a hard error rather than a silent success.Both changes are small and self-contained.