fix(config): F-C2-1 — stop os.ExpandEnv corrupting the bcrypt password_hash (v0.103.0)

loadAndParse + LoadFromBytes ran os.ExpandEnv over the whole YAML before parse;
a bcrypt hash ($2a$10$...) is full of $word sequences that get replaced with
empty env values, silently corrupting web.password_hash on load (a silent
auth-integrity bug: $2a$10$N9qo8uL... -> "a0"). Remove both ExpandEnv calls;
parse raw bytes. The typed applyEnvOverrides path (FELHOM_WEB_PASSWORD_HASH) is
the sanctioned env mechanism and is unchanged. Tests + red-proof.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
2026-07-07 18:34:52 +02:00
parent cd0ebd2042
commit 02d37a110b
3 changed files with 113 additions and 6 deletions
+18
View File
@@ -1,5 +1,23 @@
## Changelog
### v0.103.0 — F-C2-1: config loader no longer corrupts a bcrypt password_hash (silent auth bug) (2026-07-07)
Fixes campaign-2 finding **F-C2-1** (`felhom.eu/documentation/tests/CAMPAIGN-2-2026-07-07.md`).
`loadAndParse` and `LoadFromBytes` 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"`). A silent
auth-integrity bug.
- **Fix:** removed both `os.ExpandEnv` calls (`config.go` :234 loadAndParse, :249 LoadFromBytes) — 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` uses `${DOMAIN}`, which is compose-level).
- **Behavior change:** a literal `${VAR}` in a controller.yaml value is now preserved verbatim (was
expanded). No repo config depends on the old behavior.
- Tests (`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.
### v0.102.0 — async restore family: no more proxy-timeout error page on a succeeding restore (2026-07-06)
Re-adjudicates campaign **F4** (`felhom.eu/documentation/audits/RERUN-p1p3-2026-07-06.md`): all three