• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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