Scope: the whole mod as of v1.2.0 (2026-08-07), every line under src/, plus build.gradle.
Question asked: can this mod install malware, or be made to?
No, and not through any path that exists in the code.
The concern is a reasonable one to raise: the mod talks to a website and downloads files. But a download is only dangerous if something later runs it, and this mod has no way to run anything. The following were searched for across the entire source tree and are completely absent — not "used carefully", not "guarded", but zero occurrences:
| Absent | Why it would matter |
|---|---|
Runtime.exec, ProcessBuilder |
Launching a program |
System.load, System.loadLibrary |
Loading a native .dll/.so |
URLClassLoader, defineClass, setAccessible |
Loading Java code at runtime |
ScriptEngine / Nashorn |
Running a script |
ZipInputStream, ZipFile, JarFile, newFileSystem |
Unpacking an archive (so "zip-slip" cannot apply) |
Desktop.browse, Util.getPlatform().openUri/openFile |
Handing a file or link to the operating system |
ClickEvent, HoverEvent |
Clickable chat that runs a command or opens a URL |
ObjectInputStream, readObject |
Java deserialization, a classic remote-code-execution sink |
sendChatMessage, networkHandler.send |
Saying or doing anything on the Minecraft server on your behalf |
The mod never opens anything with the operating system, never launches a browser, never asks Windows to do anything with a file, and never sends a chat message or command to the game server.
There is also no update mechanism: the mod downloads no code, no jars, no plugins, and no configuration at startup. What is in the jar you installed is all there will ever be.
It is a client-only mod ("environment": "client" in fabric.mod.json). It makes HTTPS
requests to one website — the portal address you type in — and it:
- reads JSON and draws it on screen (tasks, ranks, news, map markers),
- writes three small JSON files of its own in
config/civportal/, - appends waypoints to Xaero's
waypoints.txt, if you have Xaero installed, - downloads one kind of file: a schematic, into Litematica's schematics folder, when you press a button on a build task.
That last one is the only place a file arrives from the internet, so it gets its own section.
You choose which portal to point the mod at. That portal is trusted with the data it sends you, in the same way a website is trusted with the page it serves your browser. This review is about what a portal could do if it turned hostile — not about whether it can be one.
This is the part worth actually reading.
Where it can be saved. The portal supplies a filename. Before it touches the disk it goes
through this (SchematicRenderer.java):
String cleaned = raw.replaceAll("[^A-Za-z0-9._-]", "_");
while (cleaned.startsWith(".")) cleaned = cleaned.substring(1);
return cleaned.isBlank() ? "schematic.litematic" : cleaned;Everything outside letters, digits, dot, underscore and hyphen becomes an underscore. That defeats
the whole standard list at once: ../../ (slashes replaced, leading dots stripped), C:\Windows\
(colon and backslashes replaced), file.txt:hidden (alternate data streams), embedded null bytes,
and Unicode look-alikes. The name cannot escape the folder it is written to, which is fixed at
Litematica's own schematics directory. A portal cannot choose where a file lands.
What happens to it. Nothing, until you press "Create Schematic Placement". Then the path is handed to Litematica, which parses it. This mod contains no NBT parser, no schematic parser and no decompression code of its own.
What it cannot become. A .jar written into the schematics folder is an inert file. Minecraft
loads mods from mods/ at startup and never rescans; nothing in this mod writes there, and nothing
in this mod or in Minecraft executes a file found in a schematics folder.
Your token is deliberately not sent with it. The download request is built from scratch rather
than through the normal request builder, specifically so the Authorization header does not travel
to a storage host (HttpPortalClient.java:486-496). That is the correct design and it is worth
knowing it was done on purpose.
The obvious attack, and the first thing worth suspecting: the portal replies to an authenticated
request with a redirect to evil.example, and your login token gets forwarded there.
The mod does follow redirects (followRedirects(NORMAL)) and does attach the token as a header, so
on the face of it this looks live. It is not. Java's HTTP client strips sensitive headers when a
redirect changes host:
// HttpRequestImpl.newInstanceForRedirection
if (uri.getScheme().equalsIgnoreCase(other.uri.getScheme()) &&
uri.getRawAuthority().equals(other.uri.getRawAuthority())) {
return new HttpRequestImpl(uri, method, other, mayHaveBody, Optional.empty());
}
return new HttpRequestImpl(uri, method, other, mayHaveBody, Optional.of(Utils.ALLOWED_REDIRECT_HEADERS));…where the filtered set is {Authorization, Cookie, Origin, Referer, Host}.
Verified two ways rather than taken on trust: read from the JDK 25 sources, and disassembled out of a JDK 21.0.9 runtime, because Minecraft 1.21.11 runs on Java 21 and the two could have differed. Both behave identically. No change needed.
None of these is a way to run code on your machine. They are robustness problems: what a portal that turned hostile could do to annoy you. Listed most to least worth doing.
| # | What | Effect if a portal turned hostile | Fix |
|---|---|---|---|
| 1 | JSON responses are read into memory with no size limit (HttpPortalClient.java:673) |
A huge reply crashes the game with an out-of-memory error | Cap the response body |
| 2 | The 32 MB download limit is checked after the file is fully written (HttpPortalClient.java:507-521) |
Fills the disk, bounded only by a 2-minute timeout | Check Content-Length, and stop writing at the cap |
| 3 | The "must be HTTPS" rule runs when you type an address, but not when the config file is read (PortalConfig.java:229-285) |
A shared or hand-edited config.json set to http:// sends your token in the clear. Plausible if a nation hands out a preconfigured config |
Validate on load too |
| 4 | The download URL may point at any host, and is not required to be HTTPS (HttpPortalClient.java:485-499) |
The portal can make your client fetch from arbitrary addresses, including ones on your home network | Require HTTPS; restrict to the portal's own host |
| 5 | The filename's extension is not restricted, and Litematica picks its parser by extension | The portal chooses which of Litematica's four format parsers reads its bytes | Allow only .litematic, .schem, .nbt, .schematic |
| 6 | Downloads overwrite by default (REPLACE_EXISTING) |
A portal can replace files inside its own schematics subfolder | Refuse to overwrite, or verify a hash |
| 7 | Xaero folder names strip Windows-illegal characters but not . (XaeroWaypoints.java:128-138) |
A world or server named .. writes one directory higher. You would have to do this to yourself |
Reject . and .. outright |
| 8 | If setting file permissions fails, the token is written with inherited ones (TokenStore.java:126-136) |
On a shared computer the token could be readable by another account | Fail loudly instead |
| 9 | The log records the token's first 4 characters and its exact length (CivPortalSession.java:385) |
A fingerprint, not the secret — but logs get pasted into bug reports | Log neither |
| 10 | Portal text is not stripped of § formatting codes before display |
Cosmetic vandalism in chat and toasts | Strip them on the way in |
| 11 | fabric-loom 1.17-SNAPSHOT, and no Gradle dependency verification (build.gradle:2) |
A build-time supply-chain concern, not a runtime one | Pin the version; add a verification file |
No code was changed as part of this review. These are recorded for you to decide on.
Can:
- show you false information — fake tasks, fake ranks, fake news;
- crash your game with an oversized reply (#1) or fill your disk (#2);
- write a file with a name of its choosing into Litematica's schematics folder — where it sits inert unless you ask Litematica to load it;
- make your client issue web requests to addresses it picks (#4);
- see the account you linked, and anything the mod sends it — which is your inventory ledger only if you switched that on, per container.
Cannot:
- run a program, script, or any code on your computer;
- write a file anywhere except its own subfolder of Litematica's schematics directory and the mod's own config folder;
- install a mod, replace your jar, or change anything in
mods/; - read your files, your other mods' data, or your Minecraft password;
- see where you are — the mod never transmits your position;
- read or modify your own waypoints;
- type in chat, run a command, or act as you on the Minecraft server;
- get your token forwarded to a third-party host by redirect (checked above).
Static review of all ~13,000 lines under src/, plus targeted greps for every dangerous primitive
listed in the first table, plus reading the relevant JDK sources and disassembling the JDK 21
runtime for the redirect question. The network and parsing layer contains no Minecraft types by
design, so it can also be exercised headlessly against tools/MockPortalServer.java — which is how
the download and parsing paths were run.
Anyone can repeat the first table's checks in one command:
grep -rE "Runtime\.getRuntime|ProcessBuilder|System\.load|ScriptEngine|URLClassLoader|defineClass|Desktop\.|openUri|openFile|ClickEvent|ZipInputStream|ObjectInputStream" src --include=*.javaIt returns nothing.