validate all data in repo push requests
This should get us closer towards not having to trust the server. fdroid/fdroidclient#1588 https://stackoverflow.com/questions/5205339/regular-expression-matching-fully-qualified-class-names/5205467
This commit is contained in:
		
							parent
							
								
									9a04ce4332
								
							
						
					
					
						commit
						9c8cc20a80
					
				@ -344,8 +344,7 @@ public class RepoXMLHandler extends DefaultHandler {
 | 
			
		||||
            repoDescription = cleanWhiteSpace(attributes.getValue("", "description"));
 | 
			
		||||
            repoTimestamp = parseLong(attributes.getValue("", "timestamp"), 0);
 | 
			
		||||
            repoIcon = attributes.getValue("", "icon");
 | 
			
		||||
        } else if (RepoPushRequest.INSTALL.equals(localName)
 | 
			
		||||
                || RepoPushRequest.UNINSTALL.equals(localName)) {
 | 
			
		||||
        } else if (RepoPushRequest.VALID_REQUESTS.contains(localName)) {
 | 
			
		||||
            if (repo.pushRequests == Repo.PUSH_REQUEST_ACCEPT_ALWAYS) {
 | 
			
		||||
                RepoPushRequest r = new RepoPushRequest(
 | 
			
		||||
                        localName,
 | 
			
		||||
 | 
			
		||||
@ -76,6 +76,7 @@ import java.util.List;
 | 
			
		||||
import java.util.Locale;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
import java.util.regex.Pattern;
 | 
			
		||||
 | 
			
		||||
public final class Utils {
 | 
			
		||||
 | 
			
		||||
@ -98,6 +99,8 @@ public final class Utils {
 | 
			
		||||
    private static DisplayImageOptions.Builder defaultDisplayImageOptionsBuilder;
 | 
			
		||||
    private static DisplayImageOptions repoAppDisplayImageOptions;
 | 
			
		||||
 | 
			
		||||
    private static Pattern safePackageNamePattern;
 | 
			
		||||
 | 
			
		||||
    public static final String FALLBACK_ICONS_DIR = "/icons/";
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
@ -621,6 +624,21 @@ public final class Utils {
 | 
			
		||||
        return sb;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * This is not strict validation of the package name, this is just to make
 | 
			
		||||
     * sure that the package name is not used as an attack vector, e.g. SQL
 | 
			
		||||
     * Injection.
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean isSafePackageName(@Nullable String packageName) {
 | 
			
		||||
        if (TextUtils.isEmpty(packageName)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        if (safePackageNamePattern == null) {
 | 
			
		||||
            safePackageNamePattern = Pattern.compile("[a-zA-Z0-9._]+");
 | 
			
		||||
        }
 | 
			
		||||
        return safePackageNamePattern.matcher(packageName).matches();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Calculate the number of days since the given date.
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
@ -20,6 +20,10 @@
 | 
			
		||||
package org.fdroid.fdroid.data;
 | 
			
		||||
 | 
			
		||||
import android.support.annotation.Nullable;
 | 
			
		||||
import org.fdroid.fdroid.Utils;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Represents action requests embedded in the index XML received from a repo.
 | 
			
		||||
@ -32,15 +36,33 @@ public class RepoPushRequest {
 | 
			
		||||
 | 
			
		||||
    public static final String INSTALL = "install";
 | 
			
		||||
    public static final String UNINSTALL = "uninstall";
 | 
			
		||||
    public static final List<String> VALID_REQUESTS = Arrays.asList(INSTALL, UNINSTALL);
 | 
			
		||||
 | 
			
		||||
    public final String request;
 | 
			
		||||
    public final String packageName;
 | 
			
		||||
    @Nullable
 | 
			
		||||
    public final Integer versionCode;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a new instance.  {@code request} is validated against the list of
 | 
			
		||||
     * valid install requests.  {@code packageName} has a safety validation to
 | 
			
		||||
     * make sure that only valid Android/Java Package Name characters are included.
 | 
			
		||||
     * If validation fails, the the values are set to {@code null}, which are
 | 
			
		||||
     * handled in {@link org.fdroid.fdroid.IndexV1Updater#processRepoPushRequests(List)}
 | 
			
		||||
     * or {@link org.fdroid.fdroid.IndexUpdater#processRepoPushRequests(List)}
 | 
			
		||||
     */
 | 
			
		||||
    public RepoPushRequest(String request, String packageName, @Nullable String versionCode) {
 | 
			
		||||
        this.request = request;
 | 
			
		||||
        this.packageName = packageName;
 | 
			
		||||
        if (VALID_REQUESTS.contains(request)) {
 | 
			
		||||
            this.request = request;
 | 
			
		||||
        } else {
 | 
			
		||||
            this.request = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (Utils.isSafePackageName(packageName)) {
 | 
			
		||||
            this.packageName = packageName;
 | 
			
		||||
        } else {
 | 
			
		||||
            this.packageName = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Integer i;
 | 
			
		||||
        try {
 | 
			
		||||
 | 
			
		||||
@ -160,6 +160,51 @@ public class RepoXMLHandlerTest {
 | 
			
		||||
        assertEquals(6, repoPushRequests.size());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testPushRequestsRepoCorruption() {
 | 
			
		||||
        RepoPushRequest repoPushRequest;
 | 
			
		||||
        repoPushRequest = new RepoPushRequest(null, null, null);  // request with no data
 | 
			
		||||
        assertEquals(repoPushRequest.request, null);
 | 
			
		||||
        assertEquals(repoPushRequest.packageName, null);
 | 
			
		||||
        assertEquals(repoPushRequest.versionCode, null);
 | 
			
		||||
 | 
			
		||||
        repoPushRequest = new RepoPushRequest("install", "org.fdroid.fdroid", "999999999999");
 | 
			
		||||
        assertEquals(repoPushRequest.versionCode, null);
 | 
			
		||||
 | 
			
		||||
        repoPushRequest = new RepoPushRequest("install", "org.fdroid.fdroid",
 | 
			
		||||
                String.valueOf(((long) Integer.MAX_VALUE) + 1));
 | 
			
		||||
        assertEquals(repoPushRequest.versionCode, null);
 | 
			
		||||
 | 
			
		||||
        repoPushRequest = new RepoPushRequest("install", "org.fdroid.fdroid",
 | 
			
		||||
                String.valueOf(((long) Integer.MIN_VALUE) - 1));
 | 
			
		||||
        assertEquals(repoPushRequest.versionCode, null);
 | 
			
		||||
 | 
			
		||||
        repoPushRequest = new RepoPushRequest("Robert'); DROP TABLE Students; --", "org.fdroid.fdroid", null);
 | 
			
		||||
        assertEquals(repoPushRequest.request, null);
 | 
			
		||||
        assertEquals(repoPushRequest.packageName, "org.fdroid.fdroid");
 | 
			
		||||
        assertEquals(repoPushRequest.versionCode, null);
 | 
			
		||||
 | 
			
		||||
        repoPushRequest = new RepoPushRequest("install", "Robert'); DROP TABLE Students; --", "123.1.1");
 | 
			
		||||
        assertEquals(repoPushRequest.request, "install");
 | 
			
		||||
        assertEquals(repoPushRequest.packageName, null);
 | 
			
		||||
        assertEquals(repoPushRequest.versionCode, null);
 | 
			
		||||
 | 
			
		||||
        repoPushRequest = new RepoPushRequest("install", "--", "123");
 | 
			
		||||
        assertEquals(repoPushRequest.request, "install");
 | 
			
		||||
        assertEquals(repoPushRequest.packageName, null);
 | 
			
		||||
        assertEquals(repoPushRequest.versionCode, new Integer(123));
 | 
			
		||||
 | 
			
		||||
        repoPushRequest = new RepoPushRequest("uninstall", "Robert'); DROP TABLE Students; --", "123");
 | 
			
		||||
        assertEquals(repoPushRequest.request, "uninstall");
 | 
			
		||||
        assertEquals(repoPushRequest.packageName, null);
 | 
			
		||||
        assertEquals(repoPushRequest.versionCode, new Integer(123));
 | 
			
		||||
 | 
			
		||||
        repoPushRequest = new RepoPushRequest("badrquest", "asdfasdfasdf", "123");
 | 
			
		||||
        assertEquals(repoPushRequest.request, null);
 | 
			
		||||
        assertEquals(repoPushRequest.packageName, "asdfasdfasdf");
 | 
			
		||||
        assertEquals(repoPushRequest.versionCode, new Integer(123));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testMediumRepo() {
 | 
			
		||||
        Repo expectedRepo = new Repo();
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user