Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2.0 KiB
REPORT — v0.103.0: F-C2-1 config loader no longer corrupts a bcrypt password_hash
Date: 2026-07-07 · Class: implementation (controller). Baseline: main @ cd0ebd2
(v0.102.0) → 02d37a1. Part C of the campaign-2 R1/R2 bundle.
The bug (F-C2-1, silent auth-integrity)
loadAndParse (config.go:234) and LoadFromBytes (:249) ran os.ExpandEnv over the ENTIRE YAML
before parse. A bcrypt hash ($2a$10$…) is full of $word sequences, so ExpandEnv silently
replaced each with its (usually empty) env value — corrupting web.password_hash on load. Proven:
$2a$10$N9qo8uL… → "a0". Depending on the hash this bricks login or degrades toward a bypass.
The fix
Removed both os.ExpandEnv calls — parse the raw bytes directly. The sanctioned typed env path
(applyEnvOverrides → FELHOM_WEB_PASSWORD_HASH, applied after parse) is unchanged; no shipped
controller.yaml relies on file-level ${VAR} interpolation (only docker-compose.yml's ${DOMAIN},
which is compose-level). Behavior change: a literal ${VAR} in a value is now preserved verbatim.
Tests + red-proof
config_test.go: bcrypt hash loads byte-identical (file + bytes paths) — red-proof: pre-fix
ExpandEnv mangles it to "a0" → FAIL, demonstrated + reverted; FELHOM_WEB_PASSWORD_HASH override
still wins; literal ${VAR} preserved. Full gate go build/vet/test ./... = PASS.
Deploy + live acceptance
Built + pushed :0.103.0, deployed to guest 9201 (golden/bootstrap), healthy. F-C2-1 live belt:
put a real bcrypt hash IN controller.yaml (the exact bug path), restarted → login with the correct
password succeeded (302, no "Hibás jelszó") — the hash loaded intact; pre-fix it would corrupt →
fail. Reverted controller.yaml to the auth-off pre-state (root page 200).
Observation
Migration note: any hand-edited controller.yaml carrying a ${...} that expected file-level
expansion would change behavior — none exist in the repo; the golden/bootstrap writes concrete values.