Description
ImageOptimizerSettings::normalize and ImageOptimizerProfileSet::normalize (crates/trusted-server-core/src/settings.rs) trim map keys by draining a HashMap and collecting it into a new one:
self.profiles = self
.profiles
.drain()
.map(|(key, value)| (key.trim().to_string(), value.trim().to_string()))
.filter(|(key, _)| !key.is_empty())
.collect();
When two keys trim to the same name (for example medium and " medium"), the later entry overwrites the earlier one. HashMap iteration order is randomized per instance, so the surviving value is chosen at random on every parse. The same pattern applies to profile-set names in ImageOptimizerSettings::normalize.
Fastly parses Settings on every request, so one configuration can resolve the profile differently from request to request, and template_fingerprint (and therefore the template cache key) varies with it. This is the same class of problem as #1197, with a different cause.
Reproduction
[image_optimizer.profile_sets.default_images]
default_profile = "medium"
[image_optimizer.profile_sets.default_images.profiles]
medium = "width=100"
" medium" = "width=200"
Parsing this 64 times with Settings::from_toml returned both width=100 and width=200 for profiles["medium"] across parses.
Expected behavior
Keys that collide after trimming are rejected at load time with a configuration error naming the profile set and the colliding keys, for both profile names and profile-set names. Picking a winner deterministically would still silently drop one value.
Done when
- Normalization rejects profile keys and profile-set keys that collide after trimming.
- Regression tests cover both collision sites and the non-colliding case.
Affected area
Core (Edge Cookies, GDPR): settings loading / image optimizer
Version
Reproduced at a4e01eb55 (main).
Description
ImageOptimizerSettings::normalizeandImageOptimizerProfileSet::normalize(crates/trusted-server-core/src/settings.rs) trim map keys by draining aHashMapand collecting it into a new one:When two keys trim to the same name (for example
mediumand" medium"), the later entry overwrites the earlier one.HashMapiteration order is randomized per instance, so the surviving value is chosen at random on every parse. The same pattern applies to profile-set names inImageOptimizerSettings::normalize.Fastly parses
Settingson every request, so one configuration can resolve the profile differently from request to request, andtemplate_fingerprint(and therefore the template cache key) varies with it. This is the same class of problem as #1197, with a different cause.Reproduction
Parsing this 64 times with
Settings::from_tomlreturned bothwidth=100andwidth=200forprofiles["medium"]across parses.Expected behavior
Keys that collide after trimming are rejected at load time with a configuration error naming the profile set and the colliding keys, for both profile names and profile-set names. Picking a winner deterministically would still silently drop one value.
Done when
Affected area
Core (Edge Cookies, GDPR): settings loading / image optimizer
Version
Reproduced at
a4e01eb55(main).