{"record":{"id":"adfc56b56ca6598a","repo":"HMCL-dev/HMCL","slug":"unsecure-path-adfc56","errorCode":null,"errorMessage":"Unsecure path: ","messagePattern":"Unsecure path: ","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"critical","filePath":"HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackCompletionTask.java","lineNumber":174,"sourceCode":"        Path rootPath = instance.getInstanceRoot().toAbsolutePath().normalize();\n        Map<String, ModpackConfiguration.FileInformation> files = manifest.getManifest().getFiles().stream()\n                .collect(Collectors.toMap(ModpackConfiguration.FileInformation::getPath,\n                        Function.identity()));\n\n        Set<String> remoteFiles = remoteManifest.getFiles().stream().map(ModpackConfiguration.FileInformation::getPath)\n                .collect(Collectors.toSet());\n\n        Path runDirectory = instance.getRunDirectory().toAbsolutePath().normalize();\n        Path modsDirectory = runDirectory.resolve(\"mods\");\n\n        int total = 0;\n        // for files in new modpack\n        for (ModpackConfiguration.FileInformation file : remoteManifest.getFiles()) {\n            Path actualPath = rootPath.resolve(file.getPath()).toAbsolutePath().normalize();\n            String fileName = actualPath.getFileName().toString();\n\n            if (!actualPath.startsWith(rootPath)) {\n                throw new IOException(\"Unsecure path: \" + file.getPath());\n            }\n\n            boolean download;\n\n            boolean isModDisabled = modsDirectory.equals(actualPath.getParent()) &&\n                    (Files.exists(actualPath.resolveSibling(fileName + LocalAddonManager.DISABLED_EXTENSION)) ||\n                            Files.exists(actualPath.resolveSibling(fileName + LocalAddonManager.OLD_EXTENSION)));\n\n            if (isModDisabled) {\n                download = false;\n            } else if (!files.containsKey(file.getPath())) {\n                // If old modpack does not have this entry, download it\n                download = true;\n            } else if (!Files.exists(actualPath)) {\n                // If both old and new modpacks have this entry, but the file is missing...\n                // Re-download it since network problem may cause file missing\n                download = true;\n            } else {","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackCompletionTask.java#L156-L192","documentation":"Thrown while completing/updating a server modpack when a file path declared in the remote modpack manifest resolves outside the instance root directory. HMCL normalizes each manifest entry and requires it to stay under the instance root; otherwise the path is treated as a path-traversal (zip-slip style) attack and installation aborts. This protects against malicious manifests writing files anywhere on disk.","triggerScenarios":"ServerModpackCompletionTask.execute() iterating remoteManifest.getFiles() encounters a FileInformation whose path contains '../' segments, an absolute path, or a symlink-resolved location that escapes rootPath after normalize().","commonSituations":"A maliciously or incorrectly crafted server modpack manifest with paths like '../../.minecraft/...'; manifests produced by broken exporter tools; editing manifest JSON by hand and introducing '..' segments.","solutions":["Inspect the modpack manifest (modpack.json / server.json) and fix or remove entries whose path contains '..' or is absolute.","Re-download the modpack from a trusted source; the manifest may be corrupted or tampered with.","If you are the pack author, regenerate the manifest so every path is relative and stays inside the instance directory.","Report the pack to its maintainer if the path traversal looks intentional."],"exampleFix":"// before (manifest entry)\n{\"path\": \"../../../evil.jar\", ...}\n// after\n{\"path\": \"mods/legit-mod.jar\", ...}","handlingStrategy":"validation","validationCode":"Path rootPath = instanceRoot.toAbsolutePath().normalize();\nfor (var file : manifest.getFiles()) {\n    Path p = rootPath.resolve(file.getPath()).toAbsolutePath().normalize();\n    if (!p.startsWith(rootPath))\n        throw new IllegalArgumentException(\"Unsecure path: \" + file.getPath());\n}","typeGuard":null,"tryCatchPattern":"try {\n    task.execute();\n} catch (IOException e) {\n    if (e.getMessage().startsWith(\"Unsecure path:\")) {\n        // reject/treat modpack as malicious or corrupted\n    }\n}","preventionTips":["Only install server modpacks from trusted sources.","Audit manifest paths for '..' segments or absolute paths before accepting a pack.","Never hand-edit manifest path entries."],"tags":["path-traversal","security","modpack","validation"],"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"}