1#!/usr/bin/env python 2"""Check boot jars. 3 4Usage: check_boot_jars.py <dexdump_path> <package_allow_list_file> <jar1> \ 5<jar2> ... 6""" 7import logging 8import re 9import subprocess 10import sys 11import xml.etree.ElementTree 12 13# The compiled allow list RE. 14allow_list_re = None 15 16 17def LoadAllowList(filename): 18 """ Load and compile allow list regular expressions from filename.""" 19 lines = [] 20 with open(filename, 'r') as f: 21 for line in f: 22 line = line.strip() 23 if not line or line.startswith('#'): 24 continue 25 lines.append(line) 26 combined_re = r'^(%s)$' % '|'.join(lines) 27 global allow_list_re #pylint: disable=global-statement 28 try: 29 allow_list_re = re.compile(combined_re) 30 except re.error: 31 logging.exception( 32 'Cannot compile package allow list regular expression: %r', 33 combined_re) 34 allow_list_re = None 35 return False 36 return True 37 38def CheckDexJar(dexdump_path, allow_list_path, jar): 39 """Check a dex jar file.""" 40 # Use dexdump to generate the XML representation of the dex jar file. 41 p = subprocess.Popen( 42 args='%s -l xml %s' % (dexdump_path, jar), 43 stdout=subprocess.PIPE, 44 shell=True) 45 stdout, _ = p.communicate() 46 if p.returncode != 0: 47 return False 48 49 packages = 0 50 try: 51 # TODO(b/172063475) - improve performance 52 root = xml.etree.ElementTree.fromstring(stdout) 53 except xml.etree.ElementTree.ParseError as e: 54 print('Error processing jar %s - %s' % (jar, e), file=sys.stderr) 55 print(stdout, file=sys.stderr) 56 return False 57 for package_elt in root.iterfind('package'): 58 packages += 1 59 package_name = package_elt.get('name') 60 if not package_name or not allow_list_re.match(package_name): 61 # Report the name of a class in the package as it is easier to 62 # navigate to the source of a concrete class than to a package 63 # which is often required to investigate this failure. 64 class_name = package_elt[0].get('name') 65 if package_name: 66 class_name = package_name + '.' + class_name 67 print(( 68 'Error: %s contains class file %s, whose package name "%s" is ' 69 'empty or not in the allow list %s of packages allowed on the ' 70 'bootclasspath.' 71 % (jar, class_name, package_name, allow_list_path)), 72 file=sys.stderr) 73 return False 74 if packages == 0: 75 print(('Error: %s does not contain any packages.' % jar), 76 file=sys.stderr) 77 return False 78 return True 79 80def main(argv): 81 if len(argv) < 3: 82 print(__doc__) 83 return 1 84 dexdump_path = argv[0] 85 allow_list_path = argv[1] 86 87 if not LoadAllowList(allow_list_path): 88 return 1 89 90 passed = True 91 for jar in argv[2:]: 92 if not CheckDexJar(dexdump_path, allow_list_path, jar): 93 passed = False 94 if not passed: 95 return 1 96 97 return 0 98 99 100if __name__ == '__main__': 101 sys.exit(main(sys.argv[1:])) 102