Preserve ampersands in legacy user detail sanitization
What does this MR do and why?
Saving an & in the bluesky, discord, linkedin, mastodon, orcid, twitter, or github field now stores the value as entered instead of HTML-encoded. These values stay valid once the validate_sanitizable_user_details feature flag is enabled.
Problem
Currently on master, saving one of those seven fields with an & in the value stores an HTML-encoded copy instead of the value the user entered. Any user who puts an & in one of these fields is affected. For example, a LinkedIn path of in/a&b is stored as in/a&b.
The flag-off sanitizer runs Sanitize.clean on those seven fields. Sanitize.clean always encodes &. Once the validate_sanitizable_user_details feature flag is on, the Sanitizable validator rejects any value containing an escaped HTML entity. Every row that already carries an encoded & then fails validation the next time it saves. For example, a user who once saved in/a&b as their LinkedIn value can no longer update their bio, because the stored in/a&b fails validation.
The feature flag is off by default, so nothing fails yet. Without this change, the affected rows keep accumulating until it is enabled.
Review notes
- Why not reuse
Sanitizable.sanitize, the flag-on method? It returns the input unchanged when the value contains any HTML entity. It relies on the validator to reject it. The flag-off path has no such validator. Reusing it would let raw tags like<script>alert(1)</script>&through unstripped. - Does decoding
&weaken sanitization? No. Thegsubonly turns&into&, so it cannot produce a raw<or>. Double-encoded input like&lt;still comes out single-encoded as<. The location, company, and website_url fields already work this way since thewebsite_urlampersand fix. - What about rows already saved with an encoded
&? Left alone here. A separate MR will clean them up with a batched background migration.
References
Screenshots or screen recordings
Nothing visual changed.
How to set up and validate locally
-
In a Rails console, disable the feature flag and save a field with an ampersand.
Feature.disable(:validate_sanitizable_user_details) user = User.first user.user_detail.update!(linkedin: 'in/a&b') user.user_detail.reload.linkedinField Before this MR After this MR linkedinin/a&bin/a&b -
Enable the flag and confirm the saved row now validates.
Feature.enable(:validate_sanitizable_user_details) user.user_detail.valid?Returns
true. -
Confirm tags are still stripped.
user.user_detail.update!(twitter: '<b>name</b>') user.user_detail.reload.twitterReturns
name.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.