Sanitize user input to prevent shell escape

This commit is contained in:
2026-03-12 13:33:51 +05:00
parent 9f09ac1ad0
commit 0d5a73251c
3 changed files with 52 additions and 14 deletions
@@ -76,9 +76,10 @@ fun CreateImgDialog(
val fullFileName = if (fileName.isNotBlank()) "$fileName.img" else "" val fullFileName = if (fileName.isNotBlank()) "$fileName.img" else ""
val fileExists = fullFileName.isNotEmpty() && existingFiles.contains(fullFileName) val fileExists = fullFileName.isNotEmpty() && existingFiles.contains(fullFileName)
val hasInvalidChars = fileName.any { !it.isLetterOrDigit() && it !in "-_. ()[]+," }
val isValidInput = fileName.isNotBlank() && val isValidInput = fileName.isNotBlank() &&
!fileName.contains("/") && !hasInvalidChars &&
sizeValue.toLongOrNull()?.let { it > 0 } == true && sizeValue.toLongOrNull()?.let { it > 0 } == true &&
!fileExists !fileExists
@@ -135,11 +136,11 @@ fun CreateImgDialog(
// File name input // File name input
OutlinedTextField( OutlinedTextField(
value = fileName, value = fileName,
onValueChange = { fileName = it.replace("/", "") }, onValueChange = { fileName = it },
label = { Text("File Name") }, label = { Text("File Name") },
suffix = { Text(".img") }, suffix = { Text(".img") },
singleLine = true, singleLine = true,
isError = fileExists, isError = fileExists || hasInvalidChars,
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
@@ -153,6 +154,16 @@ fun CreateImgDialog(
) )
} }
// Show error if invalid characters
if (hasInvalidChars) {
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "Invalid file name",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.error
)
}
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
// Size input // Size input
@@ -170,7 +170,8 @@ private fun RenameDialog(
val nameWithoutExtension = currentName.removeSuffix(extension) val nameWithoutExtension = currentName.removeSuffix(extension)
var newName by remember { mutableStateOf(nameWithoutExtension) } var newName by remember { mutableStateOf(nameWithoutExtension) }
val isValid = newName.isNotBlank() && !newName.contains("/") val hasInvalidChars = newName.any { !it.isLetterOrDigit() && it !in "-_. ()[]+," }
val isValid = newName.isNotBlank() && !hasInvalidChars
AlertDialog( AlertDialog(
onDismissRequest = onDismiss, onDismissRequest = onDismiss,
@@ -179,12 +180,22 @@ private fun RenameDialog(
Column { Column {
OutlinedTextField( OutlinedTextField(
value = newName, value = newName,
onValueChange = { newName = it.replace("/", "") }, onValueChange = { newName = it },
label = { Text("File Name") }, label = { Text("File Name") },
suffix = { Text(extension) }, suffix = { Text(extension) },
singleLine = true, singleLine = true,
isError = hasInvalidChars,
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
if (hasInvalidChars) {
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "Invalid file name",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.error
)
}
} }
}, },
confirmButton = { confirmButton = {
@@ -489,6 +489,7 @@ private fun DirectoryBrowserDialog(
scope.launch { scope.launch {
val trimmedName = name.trim() val trimmedName = name.trim()
if (trimmedName.isEmpty()) return@launch if (trimmedName.isEmpty()) return@launch
val newPath = "$currentPath/$trimmedName" val newPath = "$currentPath/$trimmedName"
RootManager.executeCommand("mkdir -p \"$newPath\"") RootManager.executeCommand("mkdir -p \"$newPath\"")
// Create marker file to indicate this folder was created by the app // Create marker file to indicate this folder was created by the app
@@ -716,6 +717,9 @@ private fun DirectoryBrowserDialog(
// Create folder dialog // Create folder dialog
if (showCreateFolderDialog) { if (showCreateFolderDialog) {
val hasInvalidChars = newFolderName.any { !it.isLetterOrDigit() && it !in "-_. ()[]+," }
val isValidInput = newFolderName.isNotBlank() && !hasInvalidChars
AlertDialog( AlertDialog(
onDismissRequest = { onDismissRequest = {
showCreateFolderDialog = false showCreateFolderDialog = false
@@ -723,24 +727,36 @@ private fun DirectoryBrowserDialog(
}, },
title = { Text("Create Directory") }, title = { Text("Create Directory") },
text = { text = {
OutlinedTextField( Column {
value = newFolderName, OutlinedTextField(
onValueChange = { newFolderName = it }, value = newFolderName,
label = { Text("Directory name") }, onValueChange = { newFolderName = it },
singleLine = true, label = { Text("Directory name") },
modifier = Modifier.fillMaxWidth() singleLine = true,
) isError = hasInvalidChars,
modifier = Modifier.fillMaxWidth()
)
if (hasInvalidChars) {
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "Invalid directory name",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.error
)
}
}
}, },
confirmButton = { confirmButton = {
TextButton( TextButton(
onClick = { onClick = {
if (newFolderName.isNotBlank()) { if (isValidInput) {
createFolder(newFolderName) createFolder(newFolderName)
showCreateFolderDialog = false showCreateFolderDialog = false
newFolderName = "" newFolderName = ""
} }
}, },
enabled = newFolderName.isNotBlank() enabled = isValidInput
) { ) {
Text("Create") Text("Create")
} }