Skip to content

Commit

Permalink
Perform additional URI validation in ShareRepository.
Browse files Browse the repository at this point in the history
Thanks to Shivasurya <s5sankar@uwaterloo.ca> for reporting this issue!
  • Loading branch information
greyson-signal authored and cody-signal committed Feb 17, 2021
1 parent ba14031 commit d069d93
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 0 deletions.
Expand Up @@ -28,6 +28,7 @@
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.UriUtil;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional;

Expand Down Expand Up @@ -77,6 +78,10 @@ void getResolved(@NonNull List<Uri> uris, @NonNull Callback<Optional<ShareData>>
return ShareData.forPrimitiveTypes();
}

if (!UriUtil.isValidExternalUri(context, uri)) {
throw new IOException("Invalid external URI!");
}

mimeType = getMimeType(context, uri, mimeType);

if (PartAuthority.isLocalUri(uri)) {
Expand Down
33 changes: 33 additions & 0 deletions app/src/main/java/org/thoughtcrime/securesms/util/UriUtil.java
@@ -0,0 +1,33 @@
package org.thoughtcrime.securesms.util;

import android.content.ContentResolver;
import android.content.Context;
import android.net.Uri;

import androidx.annotation.NonNull;

import java.io.File;
import java.io.IOException;

public final class UriUtil {

/**
* Ensures that an external URI is valid and doesn't contain any references to internal files or
* any other trickiness.
*/
public static boolean isValidExternalUri(@NonNull Context context, @NonNull Uri uri) {
if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
try {
File file = new File(uri.getPath());

return file.getCanonicalPath().equals(file.getPath()) &&
!file.getCanonicalPath().startsWith("/data") &&
!file.getCanonicalPath().contains(context.getPackageName());
} catch (IOException e) {
return false;
}
} else {
return true;
}
}
}
@@ -0,0 +1,55 @@
package org.thoughtcrime.securesms.util;

import android.app.Application;
import android.content.Context;
import android.net.Uri;

import androidx.test.core.app.ApplicationProvider;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.ParameterizedRobolectricTestRunner;
import org.robolectric.annotation.Config;

import java.util.Arrays;
import java.util.Collection;

import static org.junit.Assert.assertEquals;

@RunWith(ParameterizedRobolectricTestRunner.class)
@Config(manifest = Config.NONE, application = Application.class)
public class UriUtilTest_isValidExternalUri {

private final String input;
private final boolean output;

@ParameterizedRobolectricTestRunner.Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][]{
{ "content://other.app.package.name.org/path/public.txt", true },
{ "file:///sdcard/public.txt", true },
{ "file:///data/data/org.thoughtcrime.securesms/private.txt", false },
{ "file:///any/path/with/package/name/org.thoughtcrime.securesms", false },
{ "file:///org.thoughtcrime.securesms/any/path/with/package/name", false },
{ "file:///any/path/../with/back/references/private.txt", false },
{ "file:///any/path/with/back/references/../private.txt", false },
{ "file:///../any/path/with/back/references/private.txt", false },
{ "file:///encoded/back/reference/%2F..%2F..path%2Fto%2Fprivate.txt", false },
{ "file:///public/%2E%2E%2Fprivate%2Fprivate.txt", false },
{ "file:///data/no/paths/in/data", false },
});
}

public UriUtilTest_isValidExternalUri(String input, boolean output) {
this.input = input;
this.output = output;
}

@Test
public void parse() {
Context context = ApplicationProvider.getApplicationContext();
Uri uri = Uri.parse(input);

assertEquals(output, UriUtil.isValidExternalUri(context, uri));
}
}

0 comments on commit d069d93

Please sign in to comment.