{"record":{"id":"6f9a223f6bfb3611","repo":"HMCL-dev/HMCL","slug":"theme-pack-asset-entry-contains-an-unsafe-segment","errorCode":null,"errorMessage":"Theme-pack asset entry contains an unsafe segment: ","messagePattern":"Theme-pack asset entry contains an unsafe segment: ","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackAsset.java","lineNumber":76,"sourceCode":"        Objects.requireNonNull(entryName);\n\n        String normalized = entryName.trim().replace('\\\\', '/');\n        if (normalized.isEmpty()) {\n            throw new IllegalArgumentException(\"Theme-pack asset entry is empty\");\n        }\n        if (normalized.startsWith(\"/\") || normalized.matches(\"^[A-Za-z]:.*\")) {\n            throw new IllegalArgumentException(\"Theme-pack asset entry must be relative: \" + entryName);\n        }\n        if (!normalized.startsWith(ASSETS_PREFIX)) {\n            throw new IllegalArgumentException(\"Theme-pack asset entry must be under assets/: \" + entryName);\n        }\n        if (normalized.endsWith(\"/\")) {\n            throw new IllegalArgumentException(\"Theme-pack asset entry must be a file: \" + entryName);\n        }\n\n        for (String segment : normalized.split(\"/\")) {\n            if (segment.isEmpty() || \".\".equals(segment) || \"..\".equals(segment)) {\n                throw new IllegalArgumentException(\"Theme-pack asset entry contains an unsafe segment: \" + entryName);\n            }\n            for (int i = 0; i < segment.length(); i++) {\n                char ch = segment.charAt(i);\n                if (Character.isISOControl(ch) || ch == '\\0') {\n                    throw new IllegalArgumentException(\"Theme-pack asset entry contains a control character: \" + entryName);\n                }\n            }\n        }\n        return normalized;\n    }\n}\n","sourceCodeStart":58,"sourceCodeEnd":88,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackAsset.java#L58-L88","documentation":"Each '/'-separated segment of an asset entry name must be non-empty and must not be '.' or '..'. Violations throw IllegalArgumentException(\"Theme-pack asset entry contains an unsafe segment: <name>\"). This blocks path traversal and malformed names inside the pack.","triggerScenarios":"Passing names like \"assets/../secrets.png\", \"assets//icon.png\" (empty segment), or \"assets/./x.png\" to ThemePackAsset / normalizeEntryName.","commonSituations":"Malicious or buggy pack manifests containing ../ traversal attempts; string concatenation producing double slashes or redundant dots.","solutions":["Remove '.', '..' and duplicate slashes from the entry name (normalize before calling)","Reject or sanitize pack entries containing traversal segments before lookup","Canonicalize the intended path and verify it stays under assets/"],"exampleFix":"// before\nThemePackAsset.of(\"assets/\" + userInput + \".png\"); // userInput = \"../x\"\n// after\nString safe = userInput.replace(\"\\\\\", \"/\");\nif (safe.contains(\"..\") || safe.contains(\"//\")) throw new IllegalArgumentException(\"bad name\");\nThemePackAsset.of(\"assets/\" + safe + \".png\");","handlingStrategy":"validation","validationCode":"static boolean hasSafeSegments(String name) {\n    String n = name.replace('\\\\', '/');\n    for (String seg : n.split(\"/\")) {\n        if (seg.isEmpty() || seg.equals(\".\") || seg.equals(\"..\")) return false;\n    }\n    return true;\n}","typeGuard":"static boolean isSafeSegment(String seg) {\n    return !seg.isEmpty() && !\".\".equals(seg) && !\"..\".equals(seg);\n}","tryCatchPattern":"try {\n    ThemePackAsset.of(entryName);\n} catch (IllegalArgumentException e) {\n    log.warn(\"Rejected unsafe asset entry: \" + e.getMessage());\n}","preventionTips":["Never build entry names from untrusted input without checking for '..'","Normalize and canonicalize names before lookup","Reject packs containing traversal-style entries at load time"],"tags":["security","path-traversal","theme-pack"],"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"}