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) } }