Skip to content

October 2026: first full review ​

Before the first public release we reviewed all of MMO Run: the game server, the client, the launcher, the relay, Run Market, the websites and the build pipeline. Several independent reviews read the code, and the risky findings were confirmed by running the code, not just by reading it. Every finding below is fixed in MMO Run 0.2.0.

Who was affected. No public build of MMO Run had been released, so no player or host ran an affected version. The relay at relay.mmo.run was live; it runs the fixed relay (0.2.0) since 27 October 2026, and its /healthz shows the version and build it runs.

Severity ​

SeverityMeaning
CriticalLets someone run code on other people's computers.
HighExposes passwords, takes a public service down with little effort, or lets an admin take over a server.
MediumLets a player gain powers or abuse a server they should not, or makes a real attack much easier.
LowLimited impact, needs unusual conditions, or is hardening.

Summary ​

IDSeverityAreaFindingStatus
MR-2026-01CriticalClientA server could make a joining player's client run code without askingFixed
MR-2026-02HighNetworkLogins, session tokens and chat were not encrypted in transitFixed
MR-2026-03HighServerRun Script conditions could reach beyond their sandboxFixed
MR-2026-04HighRelayOne invite could exhaust the relay's player portsFixed
MR-2026-05HighRun MarketAn unauthenticated upload could exhaust the registry's memoryFixed
MR-2026-06HighRun MarketAnyone could claim the bare name of an official packFixed
MR-2026-07MediumServerThe first account registered on a world became its ownerFixed
MR-2026-08MediumServerThe editor could read and delete files outside a world's content folderFixed
MR-2026-09MediumLauncherA player could forge lines in the launcher's control channelFixed
MR-2026-10MediumServerLogins could be blocked for everyone, and password hashing stalled the gameFixed
MR-2026-11MediumServerPlayers coming through the relay shared one address for every limitFixed
MR-2026-12MediumRelayNo limit on relay sessions per address, and invite lookups were unlimitedFixed
MR-2026-13MediumRun MarketA development sign-in mode could be switched on in productionFixed
MR-2026-14MediumPacksScenes and resources in "data only" packs could carry codeFixed
MR-2026-15MediumGameplayRelogging reset ability cooldowns and brought dead characters backFixed
MR-2026-16LowClientPack downloads and images had no size limitFixed
MR-2026-17LowClientA server-chosen zone name became part of a file pathFixed
MR-2026-18LowClientSaved logins were tied to an address rather than to a serverFixed
MR-2026-19LowPacksA pack could remove files provided by the game or other packsFixed
MR-2026-20LowServerSession tokens were stored unhashed in the world databaseFixed
MR-2026-21LowServerLogin response time revealed which usernames existFixed
MR-2026-22LowServerModeration commands could act on the wrong player or a higher rankFixed
MR-2026-23LowServerA password reset left the player's live session connectedFixed
MR-2026-24LowServerCharacter names with spaces could not be targeted by commandsFixed
MR-2026-25LowGameplayA crash at the wrong moment could duplicate or lose itemsFixed
MR-2026-26LowGameplayA stale guild invite could lock a character out of guildsFixed
MR-2026-27LowGameplayAnyone could follow another player's online status and locationFixed
MR-2026-28LowServerExtension database checks could be bypassed or mixed up two extensionsFixed
MR-2026-29LowRelayForwarded-address headers were trusted from anyoneFixed
MR-2026-30LowRelayInvites could point at loopback or other unusable addressesFixed
MR-2026-31LowRelayRelay client ids were predictableFixed
MR-2026-32LowRelayError replies and the status page said more than neededFixed
MR-2026-33LowRun MarketPack signatures covered the bytes but not the pack's name or versionFixed
MR-2026-34LowRun MarketThe admin token was compared in a way that leaks timingFixed
MR-2026-35LowRun MarketSign-in was not rate limited and tokens never expiredFixed
MR-2026-36LowRun MarketSearch wildcards and paging were not boundedFixed
MR-2026-37LowRun MarketPack file types were refused by a list of bad types instead of a list of good onesFixed
MR-2026-38LowClientTesting options worked in release buildsFixed
MR-2026-39LowWebsitesNo Content Security Policy or HSTS; Run Market sent no security headersFixed
MR-2026-40LowWebsitesThe docs site published every new page by defaultFixed
MR-2026-41LowBuildThe build pipeline's permissions, action versions and downloads were not pinnedFixed

Details ​

MR-2026-01: a server could make a joining player's client run code without asking (Critical) ​

What was wrong. Worlds send players the extensions they use. The client downloaded them, checked them only against a hash the same server provided, and ran their code. A pack's "official" label also came from the server. The world's own content pack was mounted so that it could replace any file of the game. Anyone could post an invite to a hostile world and run code on the computers of everyone who joined.

What changed. The client reads every pack before using it: each file must sit inside the pack's own folder and be an allowed type, and style packs and world content may hold no code. Before downloading any pack with code, the client asks the player, showing what the pack is and whether Run Market signed it; the answer is remembered per world and per exact pack. The client checks Run Market's signature itself, so the server cannot claim a pack is official. See How you are protected.

MR-2026-02: logins and chat were not encrypted (High) ​

What was wrong. Game traffic, including the password at login and the session token on reconnect, crossed the network in the clear. Anyone on the same network, and the relay for relayed worlds, could read them.

What changed. All game traffic is encrypted with DTLS. Every world has its own certificate, which clients pin: invite codes carry it from the first connection, and a world joined by address is trusted on first contact and checked every time after.

MR-2026-03: Run Script could reach beyond its sandbox (High) ​

What was wrong. Conditions in Run Script, the quest and event language, are evaluated by the engine's expression evaluator. Through one object the script context exposed, a condition could reach the rest of the server. An admin with editor rights could therefore make themselves owner or crash the server.

What changed. Every condition is checked before it is compiled: only literals, comparisons, and/or/not and calls to the documented functions are accepted. Functions extensions add can only return plain values.

MR-2026-04: one invite could exhaust the relay (High) ​

What was wrong. A burst of registration messages for a single invite opened a new player port for each one and kept them all, so one person could use up every port and stop the relay from serving anyone until it restarted.

What changed. Registrations for an invite share one session. The relay also caps sessions per address and in total (see MR-2026-12).

MR-2026-05: an unauthenticated upload could exhaust Run Market (High) ​

What was wrong. The registry read an upload into memory before checking who sent it, and an upload without a declared length was read up to the size limit. A few dozen parallel requests could exhaust the server's memory.

What changed. Uploads are refused before any of the body is read unless they are signed in, declare their length and fit the limit. Uploads in flight are capped per account and in total, and failed attempts count toward a daily quota.

MR-2026-06: anyone could claim the bare name of an official pack (High) ​

What was wrong. Official packs are listed as run.<name>, but the bare <name> was free for anyone to take, and lookups by bare name preferred the community pack. Someone could publish "chat" and be installed in place of the official Chat extension.

What changed. The names of every official extension, and any name an official pack uses, are reserved (look-alikes too), and a bare name always means the official pack when one exists.

MR-2026-07: the first account became the owner (Medium) ​

What was wrong. A new world gave owner rights to whoever registered first. A world that went online before its host made an account could be taken over by a stranger.

What changed. A world has no owner until someone types the one-time claim code that only the host sees, in the server console and the launcher. See Claiming your world.

MR-2026-08: the editor could reach files outside the content folder (Medium) ​

What was wrong. Two of the in-game editor's messages built file paths from values they did not check, so an admin (not only the owner) could delete or read files elsewhere on the server.

What changed. Every editor message only accepts the content kinds it edits and ids of the documented form, and paths are checked again where they are built.

MR-2026-09: forged lines in the launcher's control channel (Medium) ​

What was wrong. The launcher reads the server's output line by line. A connecting player who had not logged in could make the server log text containing a line break, and the launcher would read the second line as a status or reply from the server.

What changed. Every log line is a single line, with control characters replaced, and the launcher only accepts control lines from the server's normal output.

MR-2026-10: logins could be blocked, and hashing stalled the game (Medium) ​

What was wrong. One server-wide limit on login attempts let two people keep everyone else out, and password hashing ran on the game's main loop.

What changed. Hashing runs on worker threads, cheap token logins are not limited with password logins, failed logins lock out the address they come from, and new accounts are limited per address per hour.

MR-2026-11: relayed players shared one address (Medium) ​

What was wrong. Everyone joining through the relay appeared to the world as the same local address, which is exempt from per-address limits, and the host's relay link accepted messages from anyone who knew the invite code.

What changed. The relay tells the world each player's real address, every per-address limit uses it, and the relay link only accepts messages from the relay itself.

MR-2026-12: unlimited relay sessions and lookups (Medium) ​

What changed. Sessions are capped per address and in total, invite lookups are rate limited per address, and IPv6 limits apply to the whole /64.

MR-2026-13: development sign-in in production (Medium) ​

What was wrong. Run Market's example configuration enabled a development sign-in that hands out tokens for any name, including official ones.

What changed. The registry refuses to start with development sign-in in production, and it never grants official rights unless separately enabled.

MR-2026-14: code inside "data only" packs (Medium) ​

What was wrong. Godot scenes and resources can embed scripts. Run Market allowed them in style packs and in extensions without client code, and the game loaded them from style packs.

What changed. Packs may only contain an allowed set of file types, style and content packs may contain no scenes or resources, and the game no longer loads scenes or resources from style packs.

MR-2026-15: cooldown resets and coming back from the dead (Medium) ​

What changed. Ability cooldowns are saved with the character, and a character that logs out dead comes back at the respawn point, as after any death.

Low severity ​

  • MR-2026-16: pack downloads are capped at 64 MiB, and images larger than 4096 pixels on a side are refused from their header, before decoding.
  • MR-2026-17: the minimap's saved-map file name is sanitized.
  • MR-2026-18: saved logins are tied to the world's certificate.
  • MR-2026-19: packs that remove files, or that contain encrypted entries, are refused.
  • MR-2026-20: session tokens are stored as hashes.
  • MR-2026-21: unknown usernames take as long to refuse as wrong passwords.
  • MR-2026-22: bans pick the account and its connection from one lookup, and unban and mute check rank; mutes apply to the account, not one character.
  • MR-2026-23: a password reset also disconnects the player.
  • MR-2026-24: new character names cannot contain spaces.
  • MR-2026-25: items moving between a bag and mail, a guild bank, worn gear or a house are saved together, in one transaction.
  • MR-2026-26: guild invites expire and are re-checked when accepted, and guild creation is all or nothing.
  • MR-2026-27: online status and location are shared only between mutual friends.
  • MR-2026-28: an extension's database statements are checked against every table they name, and extensions with overlapping names no longer share tables.
  • MR-2026-29 to 32: the relay trusts forwarded addresses only from its own proxy, refuses unusable invite addresses, uses random client ids, and keeps error details and status to itself.
  • MR-2026-33: Run Market signs a statement naming the pack, version and trust tier, with a key type the game can verify.
  • MR-2026-34 to 36: the admin token is compared in constant time, sign-in is rate limited, tokens expire and can be revoked, and search and paging are bounded.
  • MR-2026-37: packs may only contain the file types the game itself packs and accepts.
  • MR-2026-38: release builds ignore the testing options.
  • MR-2026-39: every site sends a strict Content Security Policy, HSTS and related headers.
  • MR-2026-40: the docs site publishes only pages on an explicit list.
  • MR-2026-41: the build pipeline runs with read-only permissions and actions pinned to exact versions, verifies the engine it downloads, and removes its deploy key after use.

Checking the fixes ​

After the fixes were written, a separate review tried to break them, with test servers and hand-made packs rather than by reading alone. It found seven ways around the new defenses, and all are fixed in 0.2.0:

  • On a first connection, a server could name another world's certificate without proving it held that world's key. The client now reconnects insisting on the named certificate, so the encrypted handshake proves it, before a saved login, a password or a trust decision is used.
  • A player's agreement to run a pack covered its name rather than its exact contents. It now covers one exact version, and a server cannot list the same pack twice.
  • Packs a previous server sent, which stay loaded until the game is closed, could be picked up again by the next server. They are now only reused as that exact version, with fresh consent.
  • A pack-format flag that patches existing files was not refused. Any file flag now is.
  • An extension's database statements could name a core table in single quotes. Quoted names count now, and extensions may not create triggers or views.
  • Relayed players whose address had not arrived yet counted as local, and IPv6 limits counted single addresses. Unknown relayed players are never local now, and IPv6 counts per /64.
  • Pack ids were not validated when a server's own packs were read, and core's own pack names were usable as extension ids. Both are refused now.

What we recommend to hosts ​

  • Use MMO Run 0.2.0 or later, and claim your world as soon as it starts.
  • Install community extensions only from authors you trust; they run with your permissions.
  • Keep your world's identity/ folder with the world when you move or back it up.

MMO Run: host your own pixel-art MMO.