DEV Community

Cover image for AppleScript Injection and Directory Traversal — Security Bugs I Fixed in My Own Apps
hiyoyo
hiyoyo

Posted on

AppleScript Injection and Directory Traversal — Security Bugs I Fixed in My Own Apps

All tests run on an 8-year-old MacBook Air.
All results from shipping 7 Mac apps as a solo developer. No sponsored opinion.
Security review of my own apps found real vulnerabilities. Not theoretical — actual bugs that could affect users.
Here's what I found and how I fixed them.

AppleScript injection via ADB output
HiyokoAutoSync uses ADB to get device information. ADB output is user-controlled — an attacker with physical access to the device can craft device names containing special characters.
If that output is passed to a shell command without sanitization:
rust// Vulnerable
let cmd = format!("osascript -e 'display notification \"{}\"'", device_name);
Command::new("sh").arg("-c").arg(&cmd).spawn();
A device named "; rm -rf ~/Documents; echo " executes arbitrary commands.
Fix: never interpolate external input into shell strings. Use argument arrays:
rust// Safe
Command::new("osascript")
.args(["-e", &format!("display notification \"{}\"",
device_name.replace('"', "\\""))])
.spawn();
Better: avoid shell interpretation entirely. Pass arguments as separate strings, not a single shell command.

Directory traversal in file sync
Sync operations use paths from ADB. A malicious Android app could expose a path like ../../.ssh/id_rsa via MTP.
rust// Vulnerable
let dest = mac_base_dir.join(&android_path); // android_path = "../../.ssh/id_rsa"
// dest is now outside mac_base_dir
Fix: validate that the resolved path stays within the intended directory:
rustfn safe_join(base: &Path, relative: &Path) -> Result {
let joined = base.join(relative);
let canonical = joined.canonicalize()
.unwrap_or(joined.clone());

if !canonical.starts_with(base) {
    return Err(AppError::Security(
        "Path traversal attempt detected".into()
    ));
}

Ok(joined)
Enter fullscreen mode Exit fullscreen mode

}

Symlink attacks
A symlink in the source directory pointing outside the destination could allow writes to arbitrary locations.
rustfn is_safe_path(path: &Path) -> bool {
// Reject symlinks in sync operations
match path.symlink_metadata() {
Ok(meta) => !meta.file_type().is_symlink(),
Err(_) => false,
}
}
Skip symlinks during sync. Document this in the app — users who need symlink handling can request it explicitly.

The lesson
Security review of your own code finds real bugs. Do it before shipping, not after. The attack surface for a desktop app is smaller than a web app, but it's not zero — especially when external data (ADB output, file paths) influences file system operations.

If this was useful, a ❤️ helps more than you'd think — thanks!
Hiyoko PDF Vault → https://hiyokoko.gumroad.com/l/HiyokoPDFVault
X → @hiyoyok

Top comments (0)