HMCL-dev/HMCL · error · IOException

Malformed modpack configuration

Error message

Malformed modpack configuration

What it means

Security check during Modrinth pack completion: each file's declared path is resolved against the instance run directory and normalized; if the result escapes the run directory, an IOException 'Unsecure path' is thrown. This blocks path-traversal attacks via malicious file entries in a modpack index.

Solutions

  1. Inspect the pack's modrinth.index.json for ../ or absolute paths in files[].path and remove/correct them
  2. Re-download the pack from the official Modrinth page
  3. Only import packs from trusted authors/sources

Example fix

// before (index entry escapes instance)
{"path": "../../.minecraft/steal.txt", ...}
// after
{"path": "mods/legit-mod.jar", ...}
Defensive patterns

Strategy: validation

Validate before calling

Path p = runDirectory.resolve(entry.getPath()).toAbsolutePath().normalize();
if (!p.startsWith(runDirectory))
    throw new IllegalArgumentException("Unsafe path in pack: " + entry.getPath());

Type guard

boolean safe = runDirectory.resolve(p).toAbsolutePath().normalize().startsWith(runDirectory);

Try / catch

try { completionTask.execute(); } catch (IOException e) { if (e.getMessage().startsWith("Unsecure path")) { /* reject pack as malicious/corrupt */ } else throw e; }

Prevention

When it happens

Trigger: Importing/completing a Modrinth pack whose modrinth.index.json files[] entries contain paths like '../something' or absolute paths resolving outside the run directory.

Common situations: Downloading a maliciously crafted modpack that tries to overwrite files outside the instance; a corrupted index with malformed relative paths; symbolic-link/normalization edge cases.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10). Data as JSON: /api/errors/e2eda203ccbe80f6. Report an issue: GitHub.

Appendix: source

Thrown at HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java:156

                    if (isMinecraftDirectory(secondLayer)) {
                        return firstLayer.getName() + "/" + secondLayer.getName();
                    }
                }
            }
        }

        throw new UnsupportedModpackException(modpackName);
    }

    private static boolean isMinecraftDirectory(ArchiveFileTree.Dir<?> dir) {
        return dir.getSubDirs().containsKey("versions") && (dir.isRoot() || ".minecraft".equals(dir.getName()));
    }

    public static ModpackConfiguration<?> readModpackConfiguration(Path file) throws IOException {
        try {
            return JsonUtils.fromJsonFile(file, ModpackConfiguration.class);
        } catch (JsonParseException e) {
            throw new IOException("Malformed modpack configuration");
        }
    }

    public static Task<?> getInstallTask(HMCLGameRepository repository, ServerModpackManifest manifest, GameInstanceID instanceId, Modpack modpack) {
        ExceptionalRunnable<?> success = () -> {
            repository.refresh();
            repository.getInstance(instanceId).enableIsolation();
        };

        ExceptionalConsumer<Exception, ?> failure = ex -> {
            if (ex instanceof ModpackCompletionException && !(ex.getCause() instanceof FileNotFoundException)) {
                success.run();
                // This is tolerable and we will not delete the game
            }
        };

        return new ServerModpackRemoteInstallTask(repository.getDependency(), manifest, instanceId)
                .whenComplete(Schedulers.defaultScheduler(), success, failure)

View on GitHub (pinned to 24702dc5a0)