Appearance
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
| Severity | Meaning |
|---|---|
| Critical | Lets someone run code on other people's computers. |
| High | Exposes passwords, takes a public service down with little effort, or lets an admin take over a server. |
| Medium | Lets a player gain powers or abuse a server they should not, or makes a real attack much easier. |
| Low | Limited impact, needs unusual conditions, or is hardening. |
Summary
| ID | Severity | Area | Finding | Status |
|---|---|---|---|---|
| MR-2026-01 | Critical | Client | A server could make a joining player's client run code without asking | Fixed |
| MR-2026-02 | High | Network | Logins, session tokens and chat were not encrypted in transit | Fixed |
| MR-2026-03 | High | Server | Run Script conditions could reach beyond their sandbox | Fixed |
| MR-2026-04 | High | Relay | One invite could exhaust the relay's player ports | Fixed |
| MR-2026-05 | High | Run Market | An unauthenticated upload could exhaust the registry's memory | Fixed |
| MR-2026-06 | High | Run Market | Anyone could claim the bare name of an official pack | Fixed |
| MR-2026-07 | Medium | Server | The first account registered on a world became its owner | Fixed |
| MR-2026-08 | Medium | Server | The editor could read and delete files outside a world's content folder | Fixed |
| MR-2026-09 | Medium | Launcher | A player could forge lines in the launcher's control channel | Fixed |
| MR-2026-10 | Medium | Server | Logins could be blocked for everyone, and password hashing stalled the game | Fixed |
| MR-2026-11 | Medium | Server | Players coming through the relay shared one address for every limit | Fixed |
| MR-2026-12 | Medium | Relay | No limit on relay sessions per address, and invite lookups were unlimited | Fixed |
| MR-2026-13 | Medium | Run Market | A development sign-in mode could be switched on in production | Fixed |
| MR-2026-14 | Medium | Packs | Scenes and resources in "data only" packs could carry code | Fixed |
| MR-2026-15 | Medium | Gameplay | Relogging reset ability cooldowns and brought dead characters back | Fixed |
| MR-2026-16 | Low | Client | Pack downloads and images had no size limit | Fixed |
| MR-2026-17 | Low | Client | A server-chosen zone name became part of a file path | Fixed |
| MR-2026-18 | Low | Client | Saved logins were tied to an address rather than to a server | Fixed |
| MR-2026-19 | Low | Packs | A pack could remove files provided by the game or other packs | Fixed |
| MR-2026-20 | Low | Server | Session tokens were stored unhashed in the world database | Fixed |
| MR-2026-21 | Low | Server | Login response time revealed which usernames exist | Fixed |
| MR-2026-22 | Low | Server | Moderation commands could act on the wrong player or a higher rank | Fixed |
| MR-2026-23 | Low | Server | A password reset left the player's live session connected | Fixed |
| MR-2026-24 | Low | Server | Character names with spaces could not be targeted by commands | Fixed |
| MR-2026-25 | Low | Gameplay | A crash at the wrong moment could duplicate or lose items | Fixed |
| MR-2026-26 | Low | Gameplay | A stale guild invite could lock a character out of guilds | Fixed |
| MR-2026-27 | Low | Gameplay | Anyone could follow another player's online status and location | Fixed |
| MR-2026-28 | Low | Server | Extension database checks could be bypassed or mixed up two extensions | Fixed |
| MR-2026-29 | Low | Relay | Forwarded-address headers were trusted from anyone | Fixed |
| MR-2026-30 | Low | Relay | Invites could point at loopback or other unusable addresses | Fixed |
| MR-2026-31 | Low | Relay | Relay client ids were predictable | Fixed |
| MR-2026-32 | Low | Relay | Error replies and the status page said more than needed | Fixed |
| MR-2026-33 | Low | Run Market | Pack signatures covered the bytes but not the pack's name or version | Fixed |
| MR-2026-34 | Low | Run Market | The admin token was compared in a way that leaks timing | Fixed |
| MR-2026-35 | Low | Run Market | Sign-in was not rate limited and tokens never expired | Fixed |
| MR-2026-36 | Low | Run Market | Search wildcards and paging were not bounded | Fixed |
| MR-2026-37 | Low | Run Market | Pack file types were refused by a list of bad types instead of a list of good ones | Fixed |
| MR-2026-38 | Low | Client | Testing options worked in release builds | Fixed |
| MR-2026-39 | Low | Websites | No Content Security Policy or HSTS; Run Market sent no security headers | Fixed |
| MR-2026-40 | Low | Websites | The docs site published every new page by default | Fixed |
| MR-2026-41 | Low | Build | The build pipeline's permissions, action versions and downloads were not pinned | Fixed |
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.