mirror of
				https://github.com/f-droid/fdroidserver.git
				synced 2025-11-04 06:30:27 +03:00 
			
		
		
		
	add strict, tested validation of Android/F-Droid package names
Android has stricter rules than Java for Package Names, but anything the Python regex thinks is valid must be valid according to Java's rules too. https://developer.android.com/studio/build/application-id
This commit is contained in:
		
							parent
							
								
									3ab66efcfe
								
							
						
					
					
						commit
						9d12b1dc61
					
				
					 7 changed files with 329 additions and 14 deletions
				
			
		| 
						 | 
				
			
			@ -75,6 +75,10 @@ VERCODE_OPERATION_RE = re.compile(r'^([ 0-9/*+-]|%c)+$')
 | 
			
		|||
CERT_PATH_REGEX = re.compile(r'^META-INF/.*\.(DSA|EC|RSA)$')
 | 
			
		||||
APK_NAME_REGEX = re.compile(r'^([a-zA-Z][\w.]*)_(-?[0-9]+)_?([0-9a-f]{7})?\.apk')
 | 
			
		||||
STANDARD_FILE_NAME_REGEX = re.compile(r'^(\w[\w.]*)_(-?[0-9]+)\.\w+')
 | 
			
		||||
FDROID_PACKAGE_NAME_REGEX = re.compile(r'''^[a-f0-9]+$''', re.IGNORECASE)
 | 
			
		||||
STRICT_APPLICATION_ID_REGEX = re.compile(r'''(?:^[a-z_]+(?:\d*[a-zA-Z_]*)*)(?:\.[a-z_]+(?:\d*[a-zA-Z_]*)*)*$''')
 | 
			
		||||
VALID_APPLICATION_ID_REGEX = re.compile(r'''(?:^[a-z_]+(?:\d*[a-zA-Z_]*)*)(?:\.[a-z_]+(?:\d*[a-zA-Z_]*)*)*$''',
 | 
			
		||||
                                        re.IGNORECASE)
 | 
			
		||||
 | 
			
		||||
MAX_VERSION_CODE = 0x7fffffff  # Java's Integer.MAX_VALUE (2147483647)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1516,8 +1520,12 @@ def parse_androidmanifests(paths, app):
 | 
			
		|||
    if max_version is None:
 | 
			
		||||
        max_version = "Unknown"
 | 
			
		||||
 | 
			
		||||
    if max_package and not is_valid_java_package_name(max_package):
 | 
			
		||||
        raise FDroidException(_("Invalid package name {0}").format(max_package))
 | 
			
		||||
    if max_package:
 | 
			
		||||
        msg = _("Invalid package name {0}").format(max_package)
 | 
			
		||||
        if not is_valid_package_name(max_package):
 | 
			
		||||
            raise FDroidException(msg)
 | 
			
		||||
        elif not is_strict_application_id(max_package):
 | 
			
		||||
            logging.warning(msg)
 | 
			
		||||
 | 
			
		||||
    return (max_version, max_vercode, max_package)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1530,12 +1538,25 @@ def is_valid_package_name(name):
 | 
			
		|||
    files use the SHA-256 sum.
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    return re.match("^([a-f0-9]+|[A-Za-z_][A-Za-z_0-9.]+)$", name)
 | 
			
		||||
    return VALID_APPLICATION_ID_REGEX.match(name) is not None \
 | 
			
		||||
        or FDROID_PACKAGE_NAME_REGEX.match(name) is not None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def is_valid_java_package_name(name):
 | 
			
		||||
    """Check whether name is a valid Java package name aka Application ID"""
 | 
			
		||||
    return re.match("^[A-Za-z_][A-Za-z_0-9.]+$", name)
 | 
			
		||||
def is_strict_application_id(name):
 | 
			
		||||
    """Check whether name is a valid Android Application ID
 | 
			
		||||
 | 
			
		||||
    The Android ApplicationID is basically a Java Package Name, but
 | 
			
		||||
    with more restrictive naming rules:
 | 
			
		||||
 | 
			
		||||
    * It must have at least two segments (one or more dots).
 | 
			
		||||
    * Each segment must start with a letter.
 | 
			
		||||
    * All characters must be alphanumeric or an underscore [a-zA-Z0-9_].
 | 
			
		||||
 | 
			
		||||
    https://developer.android.com/studio/build/application-id
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    return STRICT_APPLICATION_ID_REGEX.match(name) is not None \
 | 
			
		||||
        and '.' in name
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def getsrclib(spec, srclib_dir, subdir=None, basepath=False,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -488,7 +488,13 @@ def check_for_unsupported_metadata_files(basedir=""):
 | 
			
		|||
            if not exists:
 | 
			
		||||
                print(_('"%s/" has no matching metadata file!') % f)
 | 
			
		||||
                return_value = True
 | 
			
		||||
        elif not os.path.splitext(f)[1][1:] in formats:
 | 
			
		||||
        elif os.path.splitext(f)[1][1:] in formats:
 | 
			
		||||
            packageName = os.path.splitext(os.path.basename(f))[0]
 | 
			
		||||
            if not common.is_valid_package_name(packageName):
 | 
			
		||||
                print('"' + packageName + '" is an invalid package name!\n'
 | 
			
		||||
                      + 'https://developer.android.com/studio/build/application-id')
 | 
			
		||||
                return_value = True
 | 
			
		||||
        else:
 | 
			
		||||
            print('"' + f.replace(basedir, '')
 | 
			
		||||
                  + '" is not a supported file format: (' + ','.join(formats) + ')')
 | 
			
		||||
            return_value = True
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1067,9 +1067,12 @@ def scan_apk(apk_file):
 | 
			
		|||
    else:
 | 
			
		||||
        scan_apk_aapt(apk, apk_file)
 | 
			
		||||
 | 
			
		||||
    if not common.is_valid_java_package_name(apk['packageName']):
 | 
			
		||||
    if not common.is_valid_package_name(apk['packageName']):
 | 
			
		||||
        raise BuildException(_("{appid} from {path} is not a valid Java Package Name!")
 | 
			
		||||
                             .format(appid=apk['packageName'], path=apk_file))
 | 
			
		||||
    elif not common.is_strict_application_id(apk['packageName']):
 | 
			
		||||
        logging.warning(_("{appid} from {path} is not a valid Java Package Name!")
 | 
			
		||||
                        .format(appid=apk['packageName'], path=apk_file))
 | 
			
		||||
 | 
			
		||||
    # Get the signature, or rather the signing key fingerprints
 | 
			
		||||
    logging.debug('Getting signature of {0}'.format(os.path.basename(apk_file)))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -159,8 +159,10 @@ class CommonTest(unittest.TestCase):
 | 
			
		|||
                             "debuggable APK state was not properly parsed!")
 | 
			
		||||
 | 
			
		||||
    def test_is_valid_package_name(self):
 | 
			
		||||
        for name in ["org.fdroid.fdroid",
 | 
			
		||||
        for name in ["cafebabe",
 | 
			
		||||
                     "org.fdroid.fdroid",
 | 
			
		||||
                     "org.f_droid.fdr0ID",
 | 
			
		||||
                     "SpeedoMeterApp.main",
 | 
			
		||||
                     "05041684efd9b16c2888b1eddbadd0359f655f311b89bdd1737f560a10d20fb8"]:
 | 
			
		||||
            self.assertTrue(fdroidserver.common.is_valid_package_name(name),
 | 
			
		||||
                            "{0} should be a valid package name".format(name))
 | 
			
		||||
| 
						 | 
				
			
			@ -171,18 +173,22 @@ class CommonTest(unittest.TestCase):
 | 
			
		|||
            self.assertFalse(fdroidserver.common.is_valid_package_name(name),
 | 
			
		||||
                             "{0} should not be a valid package name".format(name))
 | 
			
		||||
 | 
			
		||||
    def test_is_valid_java_package_name(self):
 | 
			
		||||
    def test_is_strict_application_id(self):
 | 
			
		||||
        """see also tests/valid-package-names/"""
 | 
			
		||||
        for name in ["org.fdroid.fdroid",
 | 
			
		||||
                     "org.f_droid.fdr0ID"]:
 | 
			
		||||
            self.assertTrue(fdroidserver.common.is_valid_java_package_name(name),
 | 
			
		||||
                            "{0} should be a valid package name".format(name))
 | 
			
		||||
            self.assertTrue(fdroidserver.common.is_strict_application_id(name),
 | 
			
		||||
                            "{0} should be a strict application id".format(name))
 | 
			
		||||
        for name in ["0rg.fdroid.fdroid",
 | 
			
		||||
                     ".f_droid.fdr0ID",
 | 
			
		||||
                     "oneword",
 | 
			
		||||
                     "cafebabe",
 | 
			
		||||
                     "SpeedoMeterApp.main",
 | 
			
		||||
                     "org.fdroid/fdroid",
 | 
			
		||||
                     "/org.fdroid.fdroid",
 | 
			
		||||
                     "05041684efd9b16c2888b1eddbadd0359f655f311b89bdd1737f560a10d20fb8"]:
 | 
			
		||||
            self.assertFalse(fdroidserver.common.is_valid_java_package_name(name),
 | 
			
		||||
                             "{0} should not be a valid package name".format(name))
 | 
			
		||||
            self.assertFalse(fdroidserver.common.is_strict_application_id(name),
 | 
			
		||||
                             "{0} should not be a strict application id".format(name))
 | 
			
		||||
 | 
			
		||||
    def test_prepare_sources(self):
 | 
			
		||||
        testint = 99999999
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										237
									
								
								tests/valid-package-names/RandomPackageNames.java
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										237
									
								
								tests/valid-package-names/RandomPackageNames.java
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,237 @@
 | 
			
		|||
 | 
			
		||||
import java.io.BufferedReader;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.FileOutputStream;
 | 
			
		||||
import java.io.Writer;
 | 
			
		||||
import java.io.InputStreamReader;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.OutputStream;
 | 
			
		||||
import java.io.OutputStreamWriter;
 | 
			
		||||
import java.io.UnsupportedEncodingException;
 | 
			
		||||
import java.nio.ByteBuffer;
 | 
			
		||||
import java.nio.CharBuffer;
 | 
			
		||||
import java.nio.IntBuffer;
 | 
			
		||||
import java.util.Random;
 | 
			
		||||
 | 
			
		||||
// apt-get install libcommons-lang3-java
 | 
			
		||||
//import org.apache.commons.lang3.RandomStringUtils;
 | 
			
		||||
 | 
			
		||||
public class RandomPackageNames {
 | 
			
		||||
 | 
			
		||||
    private static Writer validWriter;
 | 
			
		||||
    private static Writer invalidWriter;
 | 
			
		||||
 | 
			
		||||
    private static final String[] py = {
 | 
			
		||||
        "python3", "-c",
 | 
			
		||||
        "import sys,re\n"
 | 
			
		||||
        + "m = re.search(r'''"
 | 
			
		||||
        //        + "^(?:[a-z_]+(?:\\d*[a-zA-Z_]*)*)(?:\\.[a-z_]+(?:\\d*[a-zA-Z_]*)*)*$"
 | 
			
		||||
        + "^[a-z_]+(?:\\d*[a-zA-Z_]*)(?:\\.[a-z_]+(?:\\d*[a-zA-Z_]*)*)*$"
 | 
			
		||||
        + "''', sys.stdin.read())\n"
 | 
			
		||||
        + "if m is not None:\n"
 | 
			
		||||
        + "  with open('/tmp/foo', 'w') as fp:\n"
 | 
			
		||||
        + "    fp.write(m.group() + '\\n')\n"
 | 
			
		||||
        + "sys.exit(m is None)"
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    public static boolean checkAgainstPython(String packageName)
 | 
			
		||||
        throws IOException, InterruptedException {
 | 
			
		||||
 | 
			
		||||
        ProcessBuilder pb = new ProcessBuilder(py);
 | 
			
		||||
        Process process = pb.start();
 | 
			
		||||
        OutputStream output = process.getOutputStream();
 | 
			
		||||
        output.write(packageName.getBytes());
 | 
			
		||||
        output.write("\n".getBytes());
 | 
			
		||||
        output.flush();
 | 
			
		||||
        output.close();
 | 
			
		||||
 | 
			
		||||
        int exitVal = process.waitFor();
 | 
			
		||||
        return exitVal == 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static boolean isValidJavaIdentifier(String packageName) {
 | 
			
		||||
        if (packageName.length() == 0 || !Character.isJavaIdentifierStart(packageName.charAt(0))) {
 | 
			
		||||
            //System.out.println("invalid first char: '" + packageName + "'");
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        for (int codePoint : packageName.codePoints().toArray()) {
 | 
			
		||||
            if (codePoint != 46 && !Character.isJavaIdentifierPart(codePoint)) {
 | 
			
		||||
                //System.out.println("invalid char: '"
 | 
			
		||||
                //                   + new StringBuilder().appendCodePoint(codePoint).toString() + "' "
 | 
			
		||||
                //                   + codePoint);
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void write(String packageName) throws IOException {
 | 
			
		||||
        if (isValidJavaIdentifier(packageName)) {
 | 
			
		||||
            validWriter.write(packageName);
 | 
			
		||||
            validWriter.write("\n");
 | 
			
		||||
        } else {
 | 
			
		||||
            invalidWriter.write(packageName);
 | 
			
		||||
            invalidWriter.write("\n");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void compare(String packageName)
 | 
			
		||||
        throws IOException, InterruptedException {
 | 
			
		||||
        boolean python = checkAgainstPython(packageName);
 | 
			
		||||
        boolean java = isValidJavaIdentifier(packageName);
 | 
			
		||||
        if (python && !java) {
 | 
			
		||||
            System.out.println("MISMATCH: '" + packageName + "' "
 | 
			
		||||
                               + (python ? "py:✔" : "py:☹") + " "
 | 
			
		||||
                               + (java ? "ja:✔" : "ja:☹") + " ");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public static void main (String[] args)
 | 
			
		||||
        throws IOException, InterruptedException, UnsupportedEncodingException {
 | 
			
		||||
        int[] data;
 | 
			
		||||
        byte[] bytes;
 | 
			
		||||
        ByteBuffer byteBuffer;
 | 
			
		||||
        Random random = new Random();
 | 
			
		||||
 | 
			
		||||
        validWriter = new OutputStreamWriter(new FileOutputStream("valid.txt"), "UTF-8");
 | 
			
		||||
        invalidWriter = new OutputStreamWriter(new FileOutputStream("invalid.txt"), "UTF-8");
 | 
			
		||||
        
 | 
			
		||||
        //System.out.print(".");
 | 
			
		||||
 | 
			
		||||
        char[] validFirstLetters = new char[27];
 | 
			
		||||
        validFirstLetters[0] = 95; // _
 | 
			
		||||
        for (int i = 1; i < 27; i++) {
 | 
			
		||||
            validFirstLetters[i] = (char) (i + 96);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        char[] validLetters = new char[64];
 | 
			
		||||
        int j = 0;
 | 
			
		||||
        for (char c = 32; c < 123; c++) {
 | 
			
		||||
            if ((c == 46) || (c > 47 && c < 58) || (c > 64 && c < 91) || (c > 96)) {
 | 
			
		||||
                validLetters[j] = c;
 | 
			
		||||
                j++;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (File f : new File("/home/hans/code/fdroid/fdroiddata/metadata").listFiles()) {
 | 
			
		||||
            String name = f.getName();
 | 
			
		||||
            if (name.endsWith(".yml") || name.endsWith(".txt")) {
 | 
			
		||||
                compare(name.substring(0, name.length() - 4));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        compare("SpeedoMeterApp.main");
 | 
			
		||||
        compare("uk.co.turtle-player");
 | 
			
		||||
        compare("oVPb");
 | 
			
		||||
        compare(" _LS");
 | 
			
		||||
        compare("r.vq");
 | 
			
		||||
        compare("r.vQ");
 | 
			
		||||
        compare("ra.vQ");
 | 
			
		||||
        compare("s.vQ");
 | 
			
		||||
        compare("r.tQ");
 | 
			
		||||
        compare("r.vR");
 | 
			
		||||
        compare("any.any");
 | 
			
		||||
        compare("org.fdroid.fdroid");
 | 
			
		||||
        compare("me.unfollowers.droid");
 | 
			
		||||
        compare("me_.unfollowers.droid");
 | 
			
		||||
        compare("me._unfollowers.droid");
 | 
			
		||||
        compare("me.unfo11llowers.droid");
 | 
			
		||||
        compare("me11.unfollowers.droid");
 | 
			
		||||
        compare("m11e.unfollowers.droid");
 | 
			
		||||
        compare("1me.unfollowers.droid");
 | 
			
		||||
        compare("me.unfollowers23.droid");
 | 
			
		||||
        compare("me.unfollowers.droid23d");
 | 
			
		||||
        compare("me.unfollowers_.droid");
 | 
			
		||||
        compare("me.unfollowers._droid");
 | 
			
		||||
        compare("me.unfollowers_._droid");
 | 
			
		||||
        compare("me.unfollowers.droid_");
 | 
			
		||||
        compare("me.unfollowers.droid32");
 | 
			
		||||
        compare("me.unfollowers.droid/");
 | 
			
		||||
        compare("me:.unfollowers.droid");
 | 
			
		||||
        compare(":me.unfollowers.droid");
 | 
			
		||||
        compare("me.unfollowers.dro;id");
 | 
			
		||||
        compare("me.unfollowe^rs.droid");
 | 
			
		||||
        compare("me.unfollowers.droid.");
 | 
			
		||||
        compare("me.unfollowers..droid");
 | 
			
		||||
        compare("me.unfollowers.droid._");
 | 
			
		||||
        compare("me.unfollowers.11212");
 | 
			
		||||
        compare("me.1.unfollowers.11212");
 | 
			
		||||
        compare("me..unfollowers.11212");
 | 
			
		||||
        compare("abc");
 | 
			
		||||
        compare("abc.");
 | 
			
		||||
        compare(".abc");
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < 300000; i++) {
 | 
			
		||||
            String packageName;
 | 
			
		||||
 | 
			
		||||
            int count = random.nextInt(10) + 1;
 | 
			
		||||
            byte valid = (byte) random.ints(97, 122).limit(1).toArray()[0];
 | 
			
		||||
 | 
			
		||||
            // only valid
 | 
			
		||||
            data = random.ints(46, 122)
 | 
			
		||||
                .limit(count)
 | 
			
		||||
                .filter(c -> (c == 46) || (c > 47 && c < 58) || (c > 64 && c < 91) || (c > 96))
 | 
			
		||||
                .toArray();
 | 
			
		||||
            byteBuffer = ByteBuffer.allocate(data.length);
 | 
			
		||||
            for (int value : data) {
 | 
			
		||||
                byteBuffer.put((byte)value);
 | 
			
		||||
            }
 | 
			
		||||
            if (data.length > 0) {
 | 
			
		||||
                bytes = byteBuffer.array();
 | 
			
		||||
                bytes[0] = valid;
 | 
			
		||||
                packageName = new String(byteBuffer.array(), "UTF-8");
 | 
			
		||||
                //System.out.println(packageName + ": " + isValidJavaIdentifier(packageName));
 | 
			
		||||
                compare(packageName);
 | 
			
		||||
                write(packageName);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // full US-ASCII
 | 
			
		||||
            data = random.ints(32, 126).limit(count).toArray();
 | 
			
		||||
            byteBuffer = ByteBuffer.allocate(data.length);
 | 
			
		||||
            for (int value : data) {
 | 
			
		||||
                byteBuffer.put((byte)value);
 | 
			
		||||
            }
 | 
			
		||||
            bytes = byteBuffer.array();
 | 
			
		||||
            packageName = new String(bytes, "UTF-8");
 | 
			
		||||
            //System.out.println(packageName + ": " + isValidJavaIdentifier(packageName));
 | 
			
		||||
            compare(packageName);
 | 
			
		||||
            write(packageName);
 | 
			
		||||
 | 
			
		||||
            // full US-ASCII with valid first letter
 | 
			
		||||
            data = random.ints(32, 127).limit(count).toArray();
 | 
			
		||||
            byteBuffer = ByteBuffer.allocate(data.length * 4);
 | 
			
		||||
            byteBuffer.asIntBuffer().put(data);
 | 
			
		||||
            bytes = byteBuffer.array();
 | 
			
		||||
            bytes[0] = valid;
 | 
			
		||||
            packageName = new String(bytes, "UTF-8");
 | 
			
		||||
            //System.out.println(packageName + ": " + isValidJavaIdentifier(packageName));
 | 
			
		||||
            compare(packageName);
 | 
			
		||||
            write(packageName);
 | 
			
		||||
 | 
			
		||||
            // full unicode
 | 
			
		||||
            data = random.ints(32, 0xFFFD).limit(count).toArray();
 | 
			
		||||
            byteBuffer = ByteBuffer.allocate(data.length * 4);
 | 
			
		||||
            byteBuffer.asIntBuffer().put(data);
 | 
			
		||||
            packageName = new String(byteBuffer.array(), "UTF-32");
 | 
			
		||||
            //System.out.println(packageName + ": " + isValidJavaIdentifier(packageName));
 | 
			
		||||
            compare(packageName);
 | 
			
		||||
            write(packageName);
 | 
			
		||||
 | 
			
		||||
            // full unicode with valid first letter
 | 
			
		||||
            data = random.ints(32, 0xFFFD).limit(count).toArray();
 | 
			
		||||
            byteBuffer = ByteBuffer.allocate(data.length * 4);
 | 
			
		||||
            byteBuffer.asIntBuffer().put(data);
 | 
			
		||||
            bytes = byteBuffer.array();
 | 
			
		||||
            bytes[0] = 0;
 | 
			
		||||
            bytes[1] = 0;
 | 
			
		||||
            bytes[2] = 0;
 | 
			
		||||
            bytes[3] = 120;
 | 
			
		||||
            packageName = new String(bytes, "UTF-32");
 | 
			
		||||
            //System.out.println(packageName + ": " + isValidJavaIdentifier(packageName));
 | 
			
		||||
            compare(packageName);
 | 
			
		||||
            write(packageName);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        validWriter.close();
 | 
			
		||||
        invalidWriter.close();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								tests/valid-package-names/random-package-names
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										10
									
								
								tests/valid-package-names/random-package-names
									
										
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
#!/bin/sh
 | 
			
		||||
 | 
			
		||||
set -e
 | 
			
		||||
set -x
 | 
			
		||||
 | 
			
		||||
export CLASSPATH=/usr/share/java/commons-lang3.jar:.
 | 
			
		||||
 | 
			
		||||
cd $(dirname $0)
 | 
			
		||||
javac -classpath $CLASSPATH RandomPackageNames.java
 | 
			
		||||
java -classpath $CLASSPATH  RandomPackageNames
 | 
			
		||||
							
								
								
									
										32
									
								
								tests/valid-package-names/test.py
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										32
									
								
								tests/valid-package-names/test.py
									
										
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,32 @@
 | 
			
		|||
#!/usr/bin/env python3
 | 
			
		||||
 | 
			
		||||
import re
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test(packageName):
 | 
			
		||||
    m = ANDROID_APPLICATION_ID_REGEX.match(packageName.strip())
 | 
			
		||||
    return m is not None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
ANDROID_APPLICATION_ID_REGEX = re.compile(r'''(?:^[a-z_]+(?:\d*[a-zA-Z_]*)*)(?:\.[a-z_]+(?:\d*[a-zA-Z_]*)*)*$''')
 | 
			
		||||
valid = 0
 | 
			
		||||
invalid = 0
 | 
			
		||||
 | 
			
		||||
test('org.fdroid.fdroid')
 | 
			
		||||
with open('valid.txt', encoding="utf-8") as fp:
 | 
			
		||||
    for packageName in fp:
 | 
			
		||||
        packageName = packageName.strip()
 | 
			
		||||
        if not test(packageName):
 | 
			
		||||
            valid += 1
 | 
			
		||||
            # print('should be valid:', packageName)
 | 
			
		||||
 | 
			
		||||
with open('invalid.txt', encoding="utf-8") as fp:
 | 
			
		||||
    for packageName in fp:
 | 
			
		||||
        packageName = packageName.strip()
 | 
			
		||||
        if test(packageName):
 | 
			
		||||
            invalid += 1
 | 
			
		||||
            print('should be not valid: "' + packageName + '"')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
print(valid, 'Java thinks is valid, but the Android regex does not')
 | 
			
		||||
print(invalid, 'invalid mistakes')
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue