{"record":{"id":"da4c0a72aa60ee6f","repo":"HMCL-dev/HMCL","slug":"theme-pack-asset-entry-contains-a-control-characte","errorCode":null,"errorMessage":"Theme-pack asset entry contains a control character: ","messagePattern":"Theme-pack asset entry contains a control character: ","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackAsset.java","lineNumber":81,"sourceCode":"        }\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":63,"sourceCodeEnd":88,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackAsset.java#L63-L88","documentation":"Asset entry names must not contain ISO control characters (including NUL). If any character in a segment is a control char, normalizeEntryName throws IllegalArgumentException(\"Theme-pack asset entry contains a control character: <name>\"). Control characters are unsafe in ZIP entry names and file lookups.","triggerScenarios":"Passing entry names containing characters like \\0, \\n, \\t, or other ISO control codes to ThemePackAsset / normalizeEntryName, e.g. names parsed from binary data or with a trailing newline.","commonSituations":"Reading entry names from a raw byte stream or a file line with an untrimmed newline; corrupted or crafted pack manifests.","solutions":["Trim the entry name and strip control characters before use (e.g. name.chars().filter(c -> !Character.isISOControl(c)))","Fix the source that produced the dirty name (read lines, trim, decode correctly)","Regenerate/repair the theme pack manifest with clean names"],"exampleFix":"// before\nThemePackAsset.of(line); // line ends with '\\n'\n// after\nThemePackAsset.of(line.strip());","handlingStrategy":"validation","validationCode":"static String stripControlChars(String name) {\n    return name.chars().filter(c -> !Character.isISOControl(c))\n        .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)\n        .toString().strip();\n}","typeGuard":"static boolean hasNoControlChars(String s) {\n    return s.chars().noneMatch(Character::isISOControl);\n}","tryCatchPattern":"try {\n    ThemePackAsset.of(entryName);\n} catch (IllegalArgumentException e) {\n    log.warn(\"Rejected asset entry with control chars: \" + e.getMessage());\n}","preventionTips":["Trim lines read from manifests before using them as entry names","Decode names from bytes with the correct charset","Sanitize or reject names containing NUL or control characters"],"tags":["validation","theme-pack","encoding"],"backgroundTag":"invalid-identifier-format","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"}