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