reconcile: full verdicts (Parts 1-5) + H10 plaintext-secret evidence test

C3 = MOOT-by-architecture (whole-LXC PBS DR; GetAppDrivePath systemDataPath fallback).
7 HIGH survivors: C2/H5/H6/H7/H8 FIXED, C3 MOOT, H10 PARTIAL/LIVE (tag added a WARN
but still persists plaintext on crypto.Encrypt failure — failing test proves it).
MOOT bucket C1/H9/H11 + file-gone Lows confirmed gone-and-not-migrated (agent watchdog
+ agent backup checked). M/L mechanical triage tables. Merged fix list:
CTRL-001 > deploy.go slice (CTRL-T2-1 + H10) > AGENT-001 (spike) > M2.

Tallies: FIXED 5 / MOOT 11 / LIVE 1 (H10) + M2(CTRL-T3-1).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-13 18:47:29 +02:00
parent d91bb03145
commit de7c8ab358
2 changed files with 267 additions and 12 deletions
@@ -0,0 +1,55 @@
package stacks
import (
"os"
"path/filepath"
"strings"
"testing"
)
// TestSaveAppConfigH10PlaintextOnEncryptFailure is a RECONCILE evidence test for
// BUGHUNT finding H10 (v0.30.3) at commit eea235b. The `// H10 fix` comment in
// SaveAppConfig (deploy.go:661) only ADDED A WARN LOG; on a crypto.Encrypt
// failure the code still FALLS THROUGH and persists the secret value in
// plaintext:
//
// if enc, err := crypto.Encrypt(encKey, v); err == nil { ...; continue }
// } else { log.Printf("[WARN] ... saving as plaintext") } // deploy.go:662
// saveCfg.Env[k] = v // deploy.go:665 — plaintext persisted
//
// A bad-length encKey makes aes.NewCipher (inside crypto.Encrypt) return an
// error, exercising that branch. This test asserts the SAFE invariant ("a
// sensitive value must never be written to app.yaml in plaintext"). It FAILS at
// the recorded commit — evidence that the tagged fix is observability only, not
// fail-closed. Do NOT weaken this test; the fix is to RETURN an error instead of
// falling through to the plaintext write.
func TestSaveAppConfigH10PlaintextOnEncryptFailure(t *testing.T) {
dir := t.TempDir()
const secret = "supersecret-pw-do-not-leak"
cfg := &AppConfig{
Deployed: true,
Env: map[string]string{"DB_PASSWORD": secret},
}
// Non-nil but invalid-length key (5 bytes) → aes.NewCipher fails →
// crypto.Encrypt returns an error → SaveAppConfig hits the H10 branch.
badKey := []byte("short")
if err := SaveAppConfig(dir, cfg, badKey, []string{"DB_PASSWORD"}); err != nil {
// SaveAppConfig currently does NOT error on encrypt failure; if a future
// fix makes it fail-closed by returning an error, that is the desired
// behavior and this test should be updated to assert the error instead.
t.Fatalf("H10 (would-be-fixed): SaveAppConfig returned an error on encrypt failure: %v — "+
"if this is the new fail-closed behavior, update the test to assert it", err)
}
data, err := os.ReadFile(filepath.Join(dir, "app.yaml"))
if err != nil {
t.Fatalf("reading app.yaml: %v", err)
}
if strings.Contains(string(data), secret) {
t.Fatalf("H10: app.yaml contains the secret in PLAINTEXT after a crypto.Encrypt failure "+
"(SaveAppConfig logged a WARN but still persisted it at deploy.go:665). app.yaml:\n%s", data)
}
}