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
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import static com.amaze.filemanager.ui.fragments.preferencefragments.PreferencesConstants.PREFERENCE_TEXTEDITOR_NEWSTACK;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import org.slf4j.Logger;
Expand All @@ -33,6 +34,7 @@
import com.amaze.filemanager.filesystem.root.CopyFilesCommand;
import com.amaze.filemanager.ui.activities.superclasses.ThemedActivity;
import com.amaze.filemanager.ui.fragments.DbViewerFragment;
import com.amaze.filemanager.utils.Utils;

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
Expand Down Expand Up @@ -73,9 +75,9 @@ public void onCreate(Bundle savedInstanceState) {
boolean useNewStack = getBoolean(PREFERENCE_TEXTEDITOR_NEWSTACK);
getSupportActionBar().setDisplayHomeAsUpEnabled(!useNewStack);

path = getIntent().getStringExtra("path");
path = Utils.sanitizeInput(getIntent().getStringExtra("path"));

if (path == null) {
if (!isValidPath(path)) {
Toast.makeText(this, R.string.operation_not_supported, Toast.LENGTH_SHORT).show();
finish();
return;
Expand Down Expand Up @@ -163,22 +165,51 @@ protected void onDestroy() {

@Override
public boolean onPrepareOptionsMenu(Menu menu) {
toolbar.setTitle(pathFile.getName());
setToolbarTitle();
return super.onPrepareOptionsMenu(menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
onBackPressed();
toolbar.setTitle(pathFile.getName());
setToolbarTitle();
}
return super.onOptionsItemSelected(item);
}

@Override
public void onBackPressed() {
super.onBackPressed();
toolbar.setTitle(pathFile.getName());
setToolbarTitle();
}

private void setToolbarTitle() {
if (pathFile != null) {
toolbar.setTitle(pathFile.getName());
}
}

private boolean isValidPath(String path) {
// Check if the path is not null, not empty, and does not contain special characters
if (path == null || path.isEmpty()) {
return false;
}

try {
File file = new File(path);
String canonicalPath = file.getCanonicalPath();
// Prevent path traversal attacks
if (canonicalPath.contains("..")) {
return false;
}
// Check for valid database file extensions
if (!path.endsWith(".db") && !path.endsWith(".sqlite") && !path.endsWith(".sqlite3")) {
return false;
}
return file.exists() && file.isFile();
} catch (IOException e) {
return false;
}
}
}
4 changes: 4 additions & 0 deletions app/src/main/java/com/amaze/filemanager/utils/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,10 @@ public static boolean isDeviceInLandScape(Activity activity) {

/** Sanitizes input from external application to avoid any attempt of command injection */
public static String sanitizeInput(String input) {
if (input == null || input.isEmpty()) {
return input;
}

// iterate through input and keep sanitizing until it's fully injection proof
String sanitizedInput;
String sanitizedInputTemp = input;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.amaze.filemanager.ui.activities

import android.content.Intent
import android.os.Build
import android.os.Build.VERSION_CODES.LOLLIPOP
import android.os.Build.VERSION_CODES.P
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.amaze.filemanager.shadows.ShadowMultiDex
import io.mockk.every
import io.mockk.spyk
import org.awaitility.Awaitility.await
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.Robolectric
import org.robolectric.annotation.Config
import org.robolectric.shadows.ShadowStorageManager
import org.robolectric.util.ReflectionHelpers
import java.io.File
import java.util.concurrent.TimeUnit

/**
* Tests for [DatabaseViewerActivity].
*/
@RunWith(AndroidJUnit4::class)
@Config(
sdk = [LOLLIPOP, P, Build.VERSION_CODES.R],
shadows = [ShadowMultiDex::class, ShadowStorageManager::class],
)
class DatabaseViewerActivityTest {
/**
* Tests that the activity sanitizes the path in the intent to prevent malicious payloads
* from being executed.
*/
@Test
fun testSanitizePathInIntent() {
val maliciousPayload = "/sdcard/fake.db; uname -a > /sdcard/system_info.txt; echo"

val intent = Intent().putExtra("path", maliciousPayload)

val activity =
Robolectric.buildActivity(
DatabaseViewerActivity::class.java,
intent,
)
.create().start().visible().get()

await().atMost(10, TimeUnit.SECONDS).until {
activity.isFinishing &&
ReflectionHelpers.getField<File>(activity, "pathFile") == null
}
}

/**
* Tests that the activity does not crash when the path in the intent is valid.
*/
@Test
fun testValidPathInIntent() {
// Create a temporary valid database file
val validDbFile = File.createTempFile("test", ".db")
validDbFile.deleteOnExit()

val intent = Intent().putExtra("path", validDbFile.absolutePath)

val activityController =
Robolectric.buildActivity(
DatabaseViewerActivity::class.java,
intent,
)
.create()

val activity = activityController.get()
val spyActivity = spyk(activity, recordPrivateCalls = true)

// Mock the load method to do nothing
every { spyActivity invoke ("load") withArguments listOf(any<File>()) } returns Unit

activityController.start().visible()

await().atMost(5, TimeUnit.SECONDS).until {
!spyActivity.isFinishing &&
ReflectionHelpers
.getField<File>(spyActivity, "pathFile")?.absolutePath == validDbFile.absolutePath
}

// Clean up
validDbFile.delete()
}
}
Loading