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