attempt to prevent shell escape

This commit is contained in:
2026-03-13 00:25:09 +05:00
parent 4b22871ab4
commit f3dc0b65d6
3 changed files with 42 additions and 26 deletions

View File

@@ -205,9 +205,10 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
// Create directory if it doesn't exist
if (!directory.exists()) {
RootManager.executeCommand("mkdir -p \"$currentPath\"")
val safePath = RootManager.shellEscape(currentPath)
RootManager.executeCommand("mkdir -p $safePath")
// Create marker file to indicate this folder was created by the app
RootManager.executeCommand("touch \"$currentPath/.isodroiddir\"")
RootManager.executeCommand("touch $safePath/.isodroiddir")
}
// Try multiple methods to list files
@@ -220,8 +221,9 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
private suspend fun loadFilesViaFind(currentPath: String): List<IsoFile>? {
// Use find command - more reliable for getting full paths
val safePath = RootManager.shellEscape(currentPath)
val result = RootManager.executeCommand(
"find \"$currentPath\" -maxdepth 1 -type f \\( -iname '*.iso' -o -iname '*.img' \\) 2>/dev/null"
"find $safePath -maxdepth 1 -type f \\( -iname '*.iso' -o -iname '*.img' \\) 2>/dev/null"
)
if (!result.success || result.output.isBlank()) return null
@@ -231,8 +233,9 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
.mapNotNull { filePath ->
val file = File(filePath.trim())
val name = file.name
// Get file size via stat
val sizeResult = RootManager.executeCommand("stat -c %s \"$filePath\" 2>/dev/null")
// Get file size via stat with safe escaping
val safeFilePath = RootManager.shellEscape(filePath.trim())
val sizeResult = RootManager.executeCommand("stat -c %s $safeFilePath 2>/dev/null")
val size = sizeResult.output.trim().toLongOrNull() ?: 0L
IsoFile(
path = filePath.trim(),
@@ -246,8 +249,9 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
private suspend fun loadFilesViaLs(currentPath: String): List<IsoFile>? {
// Simple ls command - just get filenames
val safePath = RootManager.shellEscape(currentPath)
val result = RootManager.executeCommand(
"ls \"$currentPath\" 2>/dev/null"
"ls $safePath 2>/dev/null"
)
if (!result.success || result.output.isBlank()) return null
@@ -259,8 +263,9 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
}
.mapNotNull { name ->
val filePath = "$currentPath/$name"
// Get file size via stat
val sizeResult = RootManager.executeCommand("stat -c %s \"$filePath\" 2>/dev/null")
// Get file size via stat with safe escaping
val safeFilePath = RootManager.shellEscape(filePath)
val sizeResult = RootManager.executeCommand("stat -c %s $safeFilePath 2>/dev/null")
val size = sizeResult.output.trim().toLongOrNull() ?: 0L
IsoFile(
path = filePath,
@@ -444,13 +449,14 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
val blockSize = 1024 * 1024L // 1MB blocks
val totalBlocks = totalBytes / blockSize
var writtenBlocks = 0L
val safeFilePath = RootManager.shellEscape(filePath)
// Create file with dd in background, checking for cancellation
val result = kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.IO) {
try {
// First, create the file with truncate to reserve space indication
val createResult = RootManager.executeCommand(
"dd if=/dev/zero of=\"$filePath\" bs=1M count=0 seek=$totalBlocks 2>/dev/null"
"dd if=/dev/zero of=$safeFilePath bs=1M count=0 seek=$totalBlocks 2>/dev/null"
)
if (!createResult.success) {
@@ -461,18 +467,18 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
while (writtenBlocks < totalBlocks) {
if (CreateImgEventBus.isCancelRequested()) {
// Clean up partial file
RootManager.executeCommand("rm -f \"$filePath\"")
RootManager.executeCommand("rm -f $safeFilePath")
return@withContext false
}
// Write a chunk (up to 64MB at a time for efficiency)
val chunksToWrite = minOf(64, totalBlocks - writtenBlocks)
val chunkResult = RootManager.executeCommand(
"dd if=/dev/zero of=\"$filePath\" bs=1M count=$chunksToWrite seek=$writtenBlocks conv=notrunc 2>/dev/null"
"dd if=/dev/zero of=$safeFilePath bs=1M count=$chunksToWrite seek=$writtenBlocks conv=notrunc 2>/dev/null"
)
if (!chunkResult.success) {
RootManager.executeCommand("rm -f \"$filePath\"")
RootManager.executeCommand("rm -f $safeFilePath")
return@withContext false
}
@@ -484,7 +490,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
true
} catch (e: Exception) {
RootManager.executeCommand("rm -f \"$filePath\"")
RootManager.executeCommand("rm -f $safeFilePath")
false
}
}
@@ -508,14 +514,18 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
val oldPath = file.path
val newPath = "${oldPath.substringBeforeLast("/")}/$newName"
// Use shell-safe escaping to prevent command injection
val safeOldPath = RootManager.shellEscape(oldPath)
val safeNewPath = RootManager.shellEscape(newPath)
// Check if new file already exists
val checkResult = RootManager.executeCommand("test -f \"$newPath\" && echo exists")
val checkResult = RootManager.executeCommand("test -f $safeNewPath && echo exists")
if (checkResult.output.trim() == "exists") {
_uiState.update { it.copy(errorMessage = "File already exists: $newName") }
return@launch
}
val result = RootManager.executeCommand("mv \"$oldPath\" \"$newPath\"")
val result = RootManager.executeCommand("mv $safeOldPath $safeNewPath")
if (result.success) {
_uiState.update { it.copy(successMessage = "Renamed to $newName") }
loadFiles()
@@ -527,7 +537,8 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
fun deleteFile(file: IsoFile) {
viewModelScope.launch {
val result = RootManager.executeCommand("rm -f \"${file.path}\"")
val safePath = RootManager.shellEscape(file.path)
val result = RootManager.executeCommand("rm -f $safePath")
if (result.success) {
_uiState.update { it.copy(successMessage = "Deleted ${file.name}") }
loadFiles()