beemdevelopment/Aegis · critical · IOException
Attempted to write outside of the parent directory
Error message
Attempted to write outside of the parent directory
What it means
After parsing the pack, importPack builds the destination directory from the pack's UUID/version and verifies its canonical path stays inside Aegis's private icons base directory. If the canonical path escapes the base dir, the code refuses to write and throws this IOException, defending against ZIP-slip style attacks where crafted metadata targets paths like ../../databases.
Solutions
- Do not import icon packs from untrusted sources — this error indicates the pack is malicious or malformed
- Inspect pack.json for traversal characters (../, absolute paths) in the uuid/version-derived fields
- Obtain a clean pack.json with a canonical UUID and repackage the ZIP
- Report the malicious pack to its distributor
Defensive patterns
Strategy: validation
Validate before calling
File packDir = new File(iconsBaseDir, uuid + "/" + version);
String canonical = packDir.getCanonicalPath();
if (!canonical.startsWith(iconsBaseDir.getCanonicalPath() + File.separator)) {
throw new IconPackException("Unsafe pack path");
} Type guard
boolean isInsideParent(File child, File parent) throws IOException {
return child.getCanonicalPath().startsWith(parent.getCanonicalPath() + File.separator);
} Try / catch
try {
iconPackManager.importPack(file);
} catch (IconPackException e) {
Log.e(TAG, "Icon pack rejected (possible path traversal)", e);
UiHelper.showDialog(context, R.string.malicious_icon_pack);
} Prevention
- Only import icon packs from trusted sources
- Reject any pack.json whose identifiers contain ../ or absolute paths
- Keep the canonical-path containment check enabled in your own forks
- Scan third-party packs before redistributing
When it happens
Trigger: Importing a crafted icon pack whose UUID/version (used to build the destination path) resolve to a canonical path outside the icons base directory — e.g. an identifier containing ../ traversal segments.
Common situations: Deliberately malicious icon packs designed to overwrite the Aegis vault or other app files (covered by testMaliciousIconPackCannotOverwriteVaultFile); extremely unlikely from legitimate packs, but possible if pack.json fields were tampered with.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- Attempted to write outside of the icon pack directory
- Bad UUID format
- Unable to find pack.json in the root of the ZIP file
- Unable to find relative to the root of the ZIP file
- signatures cannot be null or empty!
AI-assisted analysis of beemdevelopment/Aegis@d6f4e5925a (2026-09-08).
Data as JSON: /api/errors/e07e844a3a7eebf0.
Report an issue: GitHub.
Appendix: source
Thrown at app/src/main/java/com/beemdevelopment/aegis/icons/IconPackManager.java:82
public IconPack importPack(File inFile) throws IconPackException {
try {
// read and parse the icon pack definition file of the icon pack
ZipFile zipFile = new ZipFile(inFile);
FileHeader packHeader = zipFile.getFileHeader(_packDefFilename);
if (packHeader == null) {
throw new IOException("Unable to find pack.json in the root of the ZIP file");
}
IconPack pack;
byte[] defBytes;
try (ZipInputStream inStream = zipFile.getInputStream(packHeader)) {
defBytes = IOUtils.readAll(inStream);
pack = IconPack.fromBytes(defBytes);
}
// create a new directory to store the icon pack, based on the UUID and version
File packDir = getIconPackDir(pack);
if (!packDir.getCanonicalPath().startsWith(_iconsBaseDir.getCanonicalPath() + File.separator)) {
throw new IOException("Attempted to write outside of the parent directory");
}
if (packDir.exists()) {
throw new IconPackExistsException(pack);
}
IconPack existingPack = getIconPackByUUID(pack.getUUID());
if (existingPack != null) {
throw new IconPackExistsException(existingPack);
}
if (!packDir.exists() && !packDir.mkdirs()) {
throw new IOException(String.format("Unable to create directories: %s", packDir.toString()));
}
// extract each of the defined icons to the icon pack directory
for (IconPack.Icon icon : pack.getIcons()) {
File destFile = new File(packDir, icon.getRelativeFilename());
if (!destFile.getCanonicalPath().startsWith(packDir.getCanonicalPath() + File.separator)) {
throw new IOException("Attempted to write outside of the icon pack directory");
}View on GitHub (pinned to d6f4e5925a)