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:
@@ -230,11 +230,13 @@ func loadAndParse(path string) (*Config, error) {
|
||||
return nil, fmt.Errorf("reading config file: %w", err)
|
||||
}
|
||||
|
||||
// Expand environment variables in the YAML
|
||||
expanded := os.ExpandEnv(string(data))
|
||||
|
||||
// F-C2-1: parse the RAW bytes — do NOT os.ExpandEnv the whole file. A bcrypt password_hash
|
||||
// ($2a$10$…) is full of `$word` sequences that ExpandEnv silently replaces with (usually empty)
|
||||
// env values, corrupting the stored hash on load (a silent auth-integrity bug). Typed env
|
||||
// overrides are the sanctioned mechanism — applyEnvOverrides (e.g. FELHOM_WEB_PASSWORD_HASH),
|
||||
// applied after parse. No shipped controller.yaml relies on file-level ${VAR} interpolation.
|
||||
cfg := &Config{}
|
||||
if err := yaml.Unmarshal([]byte(expanded), cfg); err != nil {
|
||||
if err := yaml.Unmarshal(data, cfg); err != nil {
|
||||
return nil, fmt.Errorf("parsing config file: %w", err)
|
||||
}
|
||||
|
||||
@@ -246,9 +248,9 @@ func loadAndParse(path string) (*Config, error) {
|
||||
|
||||
// LoadFromBytes parses YAML config from raw bytes (for validation without file I/O).
|
||||
func LoadFromBytes(data []byte) (*Config, error) {
|
||||
expanded := os.ExpandEnv(string(data))
|
||||
// F-C2-1: parse RAW bytes — see loadAndParse. os.ExpandEnv would corrupt a bcrypt password_hash.
|
||||
cfg := &Config{}
|
||||
if err := yaml.Unmarshal([]byte(expanded), cfg); err != nil {
|
||||
if err := yaml.Unmarshal(data, cfg); err != nil {
|
||||
return nil, fmt.Errorf("parsing config: %w", err)
|
||||
}
|
||||
applyDefaults(cfg)
|
||||
|
||||
Reference in New Issue
Block a user