Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,14 @@ dependencies {

// ZXing for QR code generation
implementation("com.google.zxing:core:3.5.2")

// Testing
testImplementation("junit:junit:4.13.2")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3")
testImplementation("io.mockk:mockk:1.13.9")
testImplementation("org.robolectric:robolectric:4.11.1")
testImplementation("androidx.test:core:1.5.0")
testImplementation("androidx.arch.core:core-testing:2.2.0")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}
49 changes: 49 additions & 0 deletions app/src/main/java/com/phenix/wirelessadb/shell/ShellExecutor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,25 @@ object ShellExecutor {

/**
* Execute shell commands using the current backend.
* Commands are validated to prevent shell injection attacks.
* @param commands Commands to execute (joined with &&)
* @return Result with stdout on success, error on failure
*/
suspend fun execute(vararg commands: String): Result<String> = withContext(Dispatchers.IO) {
// Validate commands are not empty
if (commands.isEmpty() || commands.any { it.isBlank() }) {
return@withContext Result.failure(Exception("Commands cannot be empty"))
}

// Validate each command for shell injection vulnerabilities
for (command in commands) {
val validation = validateCommand(command)
if (validation != null) {
return@withContext Result.failure(Exception("Invalid command: $validation"))
}
}

// Join validated commands safely
val cmd = commands.joinToString(" && ")
Log.d(TAG, "Executing via $backend: $cmd")

Expand All @@ -68,6 +83,40 @@ object ShellExecutor {
}
}

/**
* Validate a command for shell injection vulnerabilities.
* @param command Command to validate
* @return Error message if invalid, null if valid
*/
private fun validateCommand(command: String): String? {
// Check for dangerous shell metacharacters that enable command injection
val dangerousPatterns = mapOf(
";" to "contains command separator (;)",
"|" to "contains pipe operator (|)",
"&&" to "contains AND operator (&&)",
"||" to "contains OR operator (||)",
"`" to "contains backtick substitution",
"\$(" to "contains command substitution \$()",
"\n" to "contains newline",
"\r" to "contains carriage return",
">" to "contains output redirection (>)",
"<" to "contains input redirection (<)"
)

for ((pattern, reason) in dangerousPatterns) {
if (command.contains(pattern)) {
return reason
}
}

// Check for subshell execution
if (command.contains('(') && command.contains(')')) {
return "contains subshell syntax"
}

return null
}

/**
* Check if any privileged backend is available.
*/
Expand Down
Loading
Loading