From 3286e2437aa84cd54c0f076a0848bcda3cc1d7bb Mon Sep 17 00:00:00 2001 From: Daniel JB Clark Date: Tue, 14 Jul 2026 00:16:00 -0400 Subject: [PATCH 1/2] feat: normalize /sdcard paths to device canonical external storage ScriptIntents now normalizes /sdcard/ and /mnt/sdcard/ path prefixes to Environment.getExternalStorageDirectory() when the file doesn't exist at the given path. This is a no-op on standard Android (where /sdcard is a symlink) but fixes script execution on devices without the symlink (Fire OS, some custom ROMs). EnvironmentUtils.normalizePath() checks File(path).exists() first to avoid unnecessary remapping on devices where the given path is valid. --- .../autojs/autojs/external/ScriptIntents.kt | 3 ++- .../autojs/autojs/util/EnvironmentUtils.kt | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/autojs/autojs/external/ScriptIntents.kt b/app/src/main/java/org/autojs/autojs/external/ScriptIntents.kt index 370ca71e2..00e447fab 100644 --- a/app/src/main/java/org/autojs/autojs/external/ScriptIntents.kt +++ b/app/src/main/java/org/autojs/autojs/external/ScriptIntents.kt @@ -9,6 +9,7 @@ import org.autojs.autojs.script.JavaScriptFileSource import org.autojs.autojs.script.ScriptSource import org.autojs.autojs.script.SequenceScriptSource import org.autojs.autojs.script.StringScriptSource +import org.autojs.autojs.util.EnvironmentUtils import org.autojs.autojs.util.WorkingDirectoryUtils import org.json.JSONException import org.json.JSONObject @@ -33,7 +34,7 @@ object ScriptIntents { @JvmStatic fun handleIntent(context: Context?, intent: Intent) { - var path = getPath(intent) + var path = EnvironmentUtils.normalizePath(getPath(intent)) var script = intent.getStringExtra(EXTRA_KEY_PRE_EXECUTE_SCRIPT) if (intent.hasExtra(EXTRA_KEY_JSON)) { diff --git a/app/src/main/java/org/autojs/autojs/util/EnvironmentUtils.kt b/app/src/main/java/org/autojs/autojs/util/EnvironmentUtils.kt index f91c838b5..eae906da5 100644 --- a/app/src/main/java/org/autojs/autojs/util/EnvironmentUtils.kt +++ b/app/src/main/java/org/autojs/autojs/util/EnvironmentUtils.kt @@ -12,4 +12,31 @@ object EnvironmentUtils { val externalStoragePath: String get() = externalStorageDirectory.path + /** + * Normalize a file path that starts with /sdcard (or legacy /mnt/sdcard) to the + * canonical external storage path returned by Environment.getExternalStorageDirectory(). + * + * On standard Android, /sdcard is a symlink to /storage/emulated/0, so File(path).exists() + * returns true and no normalization occurs. On devices without the symlink + * (Fire OS, some custom ROMs), the path is remapped to the real storage root. + * + * Returns null when path is null; returns the original path when the file exists + * at that path or the normalized path doesn't resolve. + */ + @JvmStatic + fun normalizePath(path: String?): String? { + if (path == null) return null + if (File(path).exists()) return path + val externalPath = Environment.getExternalStorageDirectory().absolutePath + if (externalPath == "/sdcard") return path + val normalized = path.replaceFirst( + Regex("^/sdcard(?=/|$)"), + externalPath + ).replaceFirst( + Regex("^/mnt/sdcard(?=/|$)"), + externalPath + ) + return if (normalized != path && File(normalized).exists()) normalized else path + } + } \ No newline at end of file From 658c83b88a9e3aee13033cf445cf0c97dd99d8e3 Mon Sep 17 00:00:00 2001 From: Daniel JB Clark Date: Tue, 14 Jul 2026 00:42:23 -0400 Subject: [PATCH 2/2] fix: resolve .. traversal via canonicalPath in normalizePath --- .../autojs/autojs/util/EnvironmentUtils.kt | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/autojs/autojs/util/EnvironmentUtils.kt b/app/src/main/java/org/autojs/autojs/util/EnvironmentUtils.kt index eae906da5..80f1e59a7 100644 --- a/app/src/main/java/org/autojs/autojs/util/EnvironmentUtils.kt +++ b/app/src/main/java/org/autojs/autojs/util/EnvironmentUtils.kt @@ -17,16 +17,24 @@ object EnvironmentUtils { * canonical external storage path returned by Environment.getExternalStorageDirectory(). * * On standard Android, /sdcard is a symlink to /storage/emulated/0, so File(path).exists() - * returns true and no normalization occurs. On devices without the symlink + * returns true and canonicalPath resolves the symlink. On devices without the symlink * (Fire OS, some custom ROMs), the path is remapped to the real storage root. * - * Returns null when path is null; returns the original path when the file exists - * at that path or the normalized path doesn't resolve. + * Path traversal (..) components are resolved by calling canonicalPath on existing files. + * + * Returns null when path is null; returns the original path when neither the given + * path nor the normalized path resolves. */ @JvmStatic fun normalizePath(path: String?): String? { if (path == null) return null - if (File(path).exists()) return path + val exists = try { + val file = File(path) + file.exists() && file.canonicalPath.let { true } + } catch (_: Exception) { + false + } + if (exists) return File(path).canonicalPath val externalPath = Environment.getExternalStorageDirectory().absolutePath if (externalPath == "/sdcard") return path val normalized = path.replaceFirst( @@ -36,7 +44,13 @@ object EnvironmentUtils { Regex("^/mnt/sdcard(?=/|$)"), externalPath ) - return if (normalized != path && File(normalized).exists()) normalized else path + if (normalized == path) return path + return try { + val nf = File(normalized) + if (nf.exists()) nf.canonicalPath else path + } catch (_: Exception) { + path + } } } \ No newline at end of file