You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Actor and group gates whose for value contains URL-reserved characters — most commonly / — can be created through the admin form but cannot be toggled or cleared afterwards: every action on them returns 404.
Flag names themselves are already well-guarded (Utils.validate_flag_name/2 enforces ^\w+$ on the /flags form), but actor ids and group names go through Utils.validate/1 which only rejects blank and ?. So any actor id or group name containing / slips through creation and then becomes unmanageable.
Repro
Open any flag in the admin UI.
Add an actor with an id containing a slash, e.g. w:foo/bar.
The actor row renders with an Enable/Disable and Clear button.
Click either button → 404 instead of the expected 302.
scr-2026-04-10-15.34.15.mp4
(Same happens for group gates whose name contains a /.)
What I think is going on
The form action rendered by _actor.html.eex / _group.html.eex ends up with a literal / inside the gate segment — e.g. /flags/test_flag_one/actors/w:foo/bar. The adapter splits on /, path_info becomes ["flags", "test_flag_one", "actors", "w:foo", "bar"], the /flags/:name/actors/:actor_id route can't match a 5-segment path, and it falls through to the 404 catch-all.
From a quick read, it looks like both Templates.url_safe/1 (used by the EEx templates when interpolating actor/group for values) and the redirect_to interpolations in Router pass the gate value through without percent-encoding reserved characters.
Summary
Actor and group gates whose
forvalue contains URL-reserved characters — most commonly/— can be created through the admin form but cannot be toggled or cleared afterwards: every action on them returns 404.Flag names themselves are already well-guarded (
Utils.validate_flag_name/2enforces^\w+$on the/flagsform), but actor ids and group names go throughUtils.validate/1which only rejects blank and?. So any actor id or group name containing/slips through creation and then becomes unmanageable.Repro
w:foo/bar.scr-2026-04-10-15.34.15.mp4
(Same happens for group gates whose name contains a
/.)What I think is going on
The form action rendered by
_actor.html.eex/_group.html.eexends up with a literal/inside the gate segment — e.g./flags/test_flag_one/actors/w:foo/bar. The adapter splits on/,path_infobecomes["flags", "test_flag_one", "actors", "w:foo", "bar"], the/flags/:name/actors/:actor_idroute can't match a 5-segment path, and it falls through to the 404 catch-all.From a quick read, it looks like both
Templates.url_safe/1(used by the EEx templates when interpolating actor/groupforvalues) and theredirect_tointerpolations inRouterpass the gate value through without percent-encoding reserved characters.