ModGate checks a mod before your game loads it. It reads the mod's packages and files as data, never loading them, and reports any executable content inside: Blueprint script, calls to dangerous engine functions, native code, script files and config files. A second check audits your project and closes the LaunchURL hole that the 2026 Meccha Chameleon Workshop malware used.
It's free. It runs in the editor (Tools > ModGate) or headless in CI (-run=ModGate), with exit codes and JSON/HTML reports.
#What ModGate is, and what it isn't
It is:
- a scanner that flags and blocks executable content in mod packages;
- a project audit with a one-step fix for
LaunchURL.
It isn't:
- A sandbox. Compiled Blueprints can't be made safe by restricting what they call. The bytecode interpreter does no type checks, so a crafted Blueprint can escape any list of allowed functions. That is why the default profile blocks Blueprint script outright.
- A guarantee that data is safe to load. A clean result means ModGate found none of the kinds of executable content listed below. Malformed data can still attack the engine's own loaders, and data can still steer your game's code.
- An antivirus. It doesn't scan your computer or recognise malware by signature.
#Why it exists
In July 2026, Steam Workshop maps for Meccha Chameleon, a UE5 game, carried a Blueprint actor named like an ambient controller.
- When a match started, the actor used two stock engine functions:
GetPlatformUserDir, then the JSON plugin'sToFile. With them it wrote a batch file to the player's Documents folder. - That batch file fetched a remote-access trojan.
- An earlier proof of concept ran a
.batshipped beside the map throughLaunchURL. On Windows,LaunchURLhands non-web URLs to the shell. - The maps had passed Workshop review. Their folders held only the usual container files (
.pak,.utoc,.ucas), a preview image and metadata. There was no executable file to spot.
ModGate blocks each step of that chain:
| Step | ModGate |
|---|---|
A Blueprint that runs on BeginPlay |
M1 Blueprint script (blocked under the default data-only profile) |
It calls LaunchURL, JSON ToFile and GetPlatformUserDir |
M2 dangerous engine function (found through the package's import table) |
| A command line stored as a Blueprint string | M5 command text |
A .bat next to the map |
M4 script file |
LaunchURL opening file:// paths |
Audit A1, fixed by -Audit -Fix |

#Quick start
In the editor:
- Tools > ModGate > Check Mod Folder… or Check Mod File… scans in a separate process, so a malformed file can't take the editor down, then opens the HTML report.
- Tools > ModGate > Audit This Project checks your mod attack surface.
- Tools > ModGate > Restrict LaunchURL to https… writes the fix.

Headless:
UnrealEditor-Cmd.exe MyGame.uproject -run=ModGate -Package="D:\Workshop\3765145606"
UnrealEditor-Cmd.exe MyGame.uproject -run=ModGate -Package=Mod.zip -Profile=blueprint-mods -Report=out\mod.json
UnrealEditor-Cmd.exe MyGame.uproject -run=ModGate -Audit
UnrealEditor-Cmd.exe MyGame.uproject -run=ModGate -Audit -Fix -AllowSchemes=https,steam

Run it from your game's own project, or from your mod kit. Then every game class and plugin a mod might use is loaded, and ModGate can name every native reference (see M9).
#Inputs
-Package= takes any of these:
- A folder, such as a Steam Workshop item or an unpacked download. It's scanned recursively.
- A container: a
.pak, or an IoStore.utoc/.ucas. - A
.zip, such as a mod.io download. It's extracted toSaved/ModGate/Extractand the extracted copy is deleted after the scan. Entries whose paths leave the folder ("zip slip") aren't extracted and are reported. - A package file:
.uassetor.umap, plus its.uexp.
#Exit codes
| Code | Meaning |
|---|---|
| 0 | Clean: no executable content found |
| 1 | Warnings to review (-FailOnWarn turns these into 2) |
| 2 | Blocked |
| 3 | Input or report error (missing file, bad policy, unknown profile) |
| anything else | The process stopped. Unreal's own container readers treat some malformed files as fatal errors. Treat it as blocked. |
#Reports
Reports go to <input>.modgate.json and .html by default, or wherever -Report= points. The JSON lists every finding (rule, severity, path, detail, why, fix) and an inventory of each package: classes defined, script functions, native functions used, game classes used, command text.
The HTML reports for the two test mods. Every finding says what was found, why it matters and how to fix it.
#Rules
| Rule | Title | data-only (default) |
blueprint-mods |
|---|---|---|---|
| M1 | Blueprint script: the mod defines a class or carries script functions | Block | Warn |
| M2 | Calls a dangerous engine function (policy list, matched by import hash) | Block (Warn for read-only "recon" functions) | Block |
| M3 | Native code: executable or library bytes, native file types, a plugin descriptor with code modules | Block | Block |
| M4 | Script or shell file (.bat, .ps1, .vbs, .lnk, .py, .sh, …) |
Block | Block |
| M5 | Command text inside a package (powershell, cmd /c, %TEMP%, …) |
Warn | Warn |
| M6 | Config file (.ini) inside the mod |
Block | Block |
| M7 | File type not on the policy's allow list | Block | Block |
| M8 | Could not be inspected: encrypted, damaged, another engine version, zip slip | Block, always | Block, always |
| M9 | A native reference this editor can't name | Warn | Warn |
| M10 | Shader code | Info | Info |
| M11 | Game classes the mod places (your extension points) | Info | Info |
data-only is the safe design: mods supply meshes, materials, levels and data assets, and your game's own Blueprints act on them.
blueprint-mods is harm reduction for games that already accept Blueprint mods. It still blocks known-dangerous calls, but it can't make Blueprint mods safe.
#How M1 and M2 work
ModGate reads each package's export and import tables.
- An export whose class is a class type (
BlueprintGeneratedClass,WidgetBlueprintGeneratedClass, …) or a function type is executable content (M1). - A cooked IoStore package stores each engine object it uses as a hash of the object's path. ModGate hashes the policy's dangerous functions the same way, so it recognises
KismetSystemLibrary.LaunchURLeven when the plugin that defines a function isn't loaded.
#The project audit
-run=ModGate -Audit (or the menu) writes Saved/ModGate/ProjectAudit.html.
| Check | What it looks at |
|---|---|
A1 LaunchURL allow-list |
Engine 5.5–5.8 filter UKismetSystemLibrary::LaunchURL through [SystemLibrary.LaunchURLFilter <scheme>] sections in Engine.ini, but ship none. With no section, Blueprint LaunchURL opens anything, and on Windows file:// goes to the shell. A1 asks the engine's own filter, built the way LaunchURL builds it, whether a file:// URL passes. |
| A2 Dangerous functions Blueprints can reach | Lists the policy's functions present in this build. A warning names optional plugins that expose file writes or processes, such as JsonBlueprintUtilities.ToFile. Editor-only modules are listed separately: they matter for cooked-editor mod kits, not packaged games. |
| A3 Project functions to review | Your own Blueprint-callable functions whose names suggest file, process, console or network access. |
The fix: -Audit -Fix (or Restrict LaunchURL to https…) writes this to Config/DefaultEngine.ini:
[SystemLibrary.LaunchURLFilter https]
!AllowedDomains=ClearArray
- It backs the file up once first, to
DefaultEngine.ini.modgate-backup, and running it again changes nothing. - It replaces any existing
LaunchURLFiltersections and refuses to allow thefilescheme. - It takes effect the next time the editor or game starts.
-AllowSchemes=https,steamkeeps other schemes your game opens on purpose.-AllowDomains=yourgame.comlimits the domains.
The audit on a default project, before and after the fix and an editor restart.
#Policy
The default policy is Resources/ModGatePolicy.json. To change it:
- Copy it and pass
-Policy=<path>to: - change the allowed file types;
- add your own engine or game functions to
riskyFunctions(each needs apath,category,severityandwhy); - add command text;
- set severities per profile.
- Pick a profile with
-Profile=<name>.
M8 is always a block, and a malformed policy exits with code 3.
#Use in a mod pipeline
- On upload (your Workshop or mod.io submission tool, or a moderation queue): run
-run=ModGate -Package=<staging folder> -FailOnWarnand reject anything that doesn't exit 0. - Keep the reports with the submission. The JSON is stable (
schema1). - In the game, mount only what passed. ModGate 1.0 doesn't enforce at mount time; see the roadmap.
#Limits
- Same engine version only. Cooked IoStore headers carry no version number, so ModGate reads packages cooked by the engine version it was built for. Packages from another version fail closed (M8).
- Encrypted containers can't be inspected (M8).
- Damaged downloads fail closed. Every container entry is read whole and checked against the hash its container stores: BLAKE3 per IoStore chunk, SHA-1 per pak entry, CRC-32 per zip entry. Any mismatch, truncation or unreadable block is an M8 block. ModGate reads IoStore containers with its own bounds-checked reader, because Epic's reader asserts on a cut-off
.utoc. - Pak indexes are still parsed by Epic's
FPakFile. ModGate checks the pak footer and the index's own SHA-1 first, which stops accidental damage. A deliberately crafted pak whose index hash is valid could still make Epic's reader stop the process. The editor menu therefore scans in a separate process, and CI should treat any exit code other than 0–3 as blocked. - Data can steer game code. M11 lists the game classes a mod uses. Review that they're ones you meant to expose.
#Supported versions and platforms
- Engines: UE 5.5, 5.6, 5.7 and 5.8.
- Where it runs: the Win64 editor and commandlet. Both modules also compile for Linux: every file is compiled with each engine's pinned Linux clang and the engine's warnings-as-errors set. That check is compile-only, and ModGate hasn't been run in a Linux editor. macOS isn't claimed.
- Mods for any target platform: it reads mods cooked for Windows, Linux and Android (ASTC). This was verified on all four engines with a data-only mod, a scripted mod and a whole base game.
- No game-time code: both modules are editor modules, so ModGate adds nothing to packaged games or dedicated servers. Unreal compiles editor modules only into editor targets. Verified on all four engines with ModGate enabled: Win64, Linux and Android builds package and start, in Development and Shipping, and none of ModGate's code is in their binaries.
#Roadmap (not in 1.0)
These are planned only if studios ask for them:
- a runtime gate that refuses to mount unvalidated or unsigned packages;
- Steam Workshop and mod.io upload hooks;
- allow-listing your game classes.
#Support and services
- Questions and bug reports: Discord https://discord.gg/nqYQ5mtmHb.
- Services: GregOrigin (Andras Gregori) also helps studios plan safe mod support: data-only mod design, upload pipelines and audits. Contact: https://gregorigin.com.



