{"record":{"id":"6bd6d19348ff49e7","repo":"beemdevelopment/Aegis","slug":"attempted-to-write-outside-of-the-icon-pack-direct","errorCode":null,"errorMessage":"Attempted to write outside of the icon pack directory","messagePattern":"Attempted to write outside of the icon pack directory","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"critical","filePath":"app/src/main/java/com/beemdevelopment/aegis/icons/IconPackManager.java","lineNumber":99,"sourceCode":"            if (!packDir.getCanonicalPath().startsWith(_iconsBaseDir.getCanonicalPath() + File.separator)) {\n                throw new IOException(\"Attempted to write outside of the parent directory\");\n            }\n            if (packDir.exists()) {\n                throw new IconPackExistsException(pack);\n            }\n            IconPack existingPack = getIconPackByUUID(pack.getUUID());\n            if (existingPack != null) {\n                throw new IconPackExistsException(existingPack);\n            }\n            if (!packDir.exists() && !packDir.mkdirs()) {\n                throw new IOException(String.format(\"Unable to create directories: %s\", packDir.toString()));\n            }\n\n            // extract each of the defined icons to the icon pack directory\n            for (IconPack.Icon icon : pack.getIcons()) {\n                File destFile = new File(packDir, icon.getRelativeFilename());\n                if (!destFile.getCanonicalPath().startsWith(packDir.getCanonicalPath() + File.separator)) {\n                    throw new IOException(\"Attempted to write outside of the icon pack directory\");\n                }\n                FileHeader iconHeader = zipFile.getFileHeader(icon.getRelativeFilename());\n                if (iconHeader == null) {\n                    throw new IOException(String.format(\"Unable to find %s relative to the root of the ZIP file\", icon.getRelativeFilename()));\n                }\n\n                // create new directories for this file if needed\n                File parent = destFile.getParentFile();\n                if (parent != null && !parent.exists() && !parent.mkdirs()) {\n                    throw new IOException(String.format(\"Unable to create directories: %s\", packDir.toString()));\n                }\n\n                try (ZipInputStream inStream = zipFile.getInputStream(iconHeader);\n                     FileOutputStream outStream = new FileOutputStream(destFile)) {\n                    IOUtils.copy(inStream, outStream);\n                }\n\n                // after successful copy of the icon, store the new filename","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/app/src/main/java/com/beemdevelopment/aegis/icons/IconPackManager.java#L81-L117","documentation":"For each icon declared in pack.json, importPack computes the destination file inside the pack directory and asserts its canonical path stays within that directory. If an icon's relative filename escapes the pack dir (e.g. \"../../vault.json\"), the import aborts with this IOException. Like the parent-directory check, this blocks ZIP-slip attacks on individual icon entries.","triggerScenarios":"Importing an icon pack whose pack.json declares an icon with a relative filename containing ../ traversal or an absolute path, making destFile resolve outside the pack directory.","commonSituations":"Malicious icon packs attempting to overwrite arbitrary app files (covered by testMaliciousIconPackCannotOverwriteVaultFile); hand-edited pack.json with bad filename fields; Windows-authored archives with backslash paths that canonicalize unexpectedly.","solutions":["Do not import icon packs from untrusted sources — this indicates a malicious or corrupted pack.json","Inspect the \"filename\"/relative path fields in pack.json for ../ or absolute path segments","Fix the icon filenames to plain relative names and repackage the ZIP","Re-download the pack from a trusted source"],"exampleFix":"// before (pack.json)\n{ \"filename\": \"../../../databases/aegis.db\" }\n// after (pack.json)\n{ \"filename\": \"icon.png\" }","handlingStrategy":"validation","validationCode":"File destFile = new File(packDir, icon.getRelativeFilename());\nif (!destFile.getCanonicalPath().startsWith(packDir.getCanonicalPath() + File.separator)) {\n    throw new IconPackException(\"Unsafe icon path: \" + icon.getRelativeFilename());\n}","typeGuard":"boolean isSafeRelativeName(String name) {\n    return name != null && !name.startsWith(\"/\") && !name.contains(\"..\") && !name.contains(\"\\\\\");\n}","tryCatchPattern":"try {\n    iconPackManager.importPack(file);\n} catch (IconPackException e) {\n    Log.e(TAG, \"Icon pack rejected (path traversal in icon filename)\", e);\n}","preventionTips":["Reject icon filenames containing .., leading /, or backslashes before import","Only install icon packs from vetted sources","Canonicalize paths (not string compares on raw names) for containment checks","Review pack.json contents when importing third-party packs"],"tags":["security","path-traversal","zip-slip","icon-pack"],"backgroundTag":"path-traversal-blocked","analyzedSha":"d6f4e5925a97e4e91593f1542085eae03432a759","analyzedAt":"2026-09-08T00:46:31.111Z","contentChangedAt":"2026-09-08T00:46:31.111Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}