How to match player names in admin commands
Partial names, display names and Luau patterns make commands hit the wrong player. How to match usernames exactly or refuse ambiguous matches.
What it looks like
A command finds its target from typed text with a loose match: part of a name, a display name, or the text used as a Luau pattern. Short or odd input matches whoever comes first, and a moderator's ban lands on the wrong player.
Luau
if string.find(string.lower(other.Name), string.lower(text)) then return other end
How to detect it
- Find the function that turns command text into a
Playerand read how it compares names. string.findandstring.matchtreat the text as a pattern unlessstring.findgetstrueas its fourth argument. An empty string or.matches every name, and a stray[or%raises an error.- Matching part of a name with no minimum length, or returning the first match when several players fit, picks a target the moderator didn't mean.
- Matching on
DisplayNameis risky on its own. Display names aren't unique, so two players can share one, and players choose their own. - Words like
allorothersthat expand to everyone deserve a look on kick, ban or data commands.
What an exploiter could do
A moderator types part of a rule-breaker's name and the command kicks or bans someone else. A player who knows how a game's commands match names can pick a display name that makes staff commands hit another player instead of them. If the text reaches a pattern, a stray symbol in a name can break the command for everyone using it.
How to fix it
- Match the full username, ignoring case, or a
UserId. - If you want shortcuts, compare plain prefixes (
string.suband==, orstring.findwithtrueas the fourth argument), require a few characters, and refuse when more than one player matches. - Show the moderator the matched player's username, or use a list to pick from, rather than acting on a guess.
Vulnerable example
Vulnerable
local Players = game:GetService("Players") local function findPlayer(text: string): Player? for _, other in Players:GetPlayers() do if string.find(string.lower(other.Name), string.lower(text)) then return other end end return nil end return findPlayer
Fixed example
Fixed
local Players = game:GetService("Players") local function findPlayer(text: string): Player? local wanted = string.lower(text) if #wanted < 3 then return nil end local found: Player? = nil for _, other in Players:GetPlayers() do local name = string.lower(other.Name) if name == wanted then return other end if string.sub(name, 1, #wanted) == wanted then if found then return nil end found = other end end return found end return findPlayer
Common false positives
- Commands that take a
Playerinstance from an admin panel's list, rather than typed text, don't match names at all. - Partial matching that needs a minimum length and refuses ties is fine, even if it's looser than an exact match.
- Matching that only feeds a harmless command, such as teleporting the caller to a friend, is low risk. Mention it rather than reporting it.