{"record":{"id":"e2eda203ccbe80f6","repo":"HMCL-dev/HMCL","slug":"malformed-modpack-configuration-e2eda2","errorCode":null,"errorMessage":"Malformed modpack configuration","messagePattern":"Malformed modpack configuration","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java","lineNumber":156,"sourceCode":"                    if (isMinecraftDirectory(secondLayer)) {\n                        return firstLayer.getName() + \"/\" + secondLayer.getName();\n                    }\n                }\n            }\n        }\n\n        throw new UnsupportedModpackException(modpackName);\n    }\n\n    private static boolean isMinecraftDirectory(ArchiveFileTree.Dir<?> dir) {\n        return dir.getSubDirs().containsKey(\"versions\") && (dir.isRoot() || \".minecraft\".equals(dir.getName()));\n    }\n\n    public static ModpackConfiguration<?> readModpackConfiguration(Path file) throws IOException {\n        try {\n            return JsonUtils.fromJsonFile(file, ModpackConfiguration.class);\n        } catch (JsonParseException e) {\n            throw new IOException(\"Malformed modpack configuration\");\n        }\n    }\n\n    public static Task<?> getInstallTask(HMCLGameRepository repository, ServerModpackManifest manifest, GameInstanceID instanceId, Modpack modpack) {\n        ExceptionalRunnable<?> success = () -> {\n            repository.refresh();\n            repository.getInstance(instanceId).enableIsolation();\n        };\n\n        ExceptionalConsumer<Exception, ?> failure = ex -> {\n            if (ex instanceof ModpackCompletionException && !(ex.getCause() instanceof FileNotFoundException)) {\n                success.run();\n                // This is tolerable and we will not delete the game\n            }\n        };\n\n        return new ServerModpackRemoteInstallTask(repository.getDependency(), manifest, instanceId)\n                .whenComplete(Schedulers.defaultScheduler(), success, failure)","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java#L138-L174","documentation":"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.","triggerScenarios":"Importing/completing a Modrinth pack whose modrinth.index.json files[] entries contain paths like '../something' or absolute paths resolving outside the run directory.","commonSituations":"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.","solutions":["Inspect the pack's modrinth.index.json for ../ or absolute paths in files[].path and remove/correct them","Re-download the pack from the official Modrinth page","Only import packs from trusted authors/sources"],"exampleFix":"// before (index entry escapes instance)\n{\"path\": \"../../.minecraft/steal.txt\", ...}\n// after\n{\"path\": \"mods/legit-mod.jar\", ...}","handlingStrategy":"validation","validationCode":"Path p = runDirectory.resolve(entry.getPath()).toAbsolutePath().normalize();\nif (!p.startsWith(runDirectory))\n    throw new IllegalArgumentException(\"Unsafe path in pack: \" + entry.getPath());","typeGuard":"boolean safe = runDirectory.resolve(p).toAbsolutePath().normalize().startsWith(runDirectory);","tryCatchPattern":"try { completionTask.execute(); } catch (IOException e) { if (e.getMessage().startsWith(\"Unsecure path\")) { /* reject pack as malicious/corrupt */ } else throw e; }","preventionTips":["Only import packs from trusted Modrinth authors","Scan index files[] paths for ../ before installing","Keep HMCL updated for current traversal checks"],"tags":["security","path-traversal","modrinth"],"backgroundTag":"path-traversal-blocked","analyzedSha":"24702dc5a0214034f4c27166d5fd30cad08cec19","analyzedAt":"2026-09-10T12:36:46.680Z","contentChangedAt":"2026-09-10T12:36:46.680Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}