1#!/usr/bin/env python
2
3import re
4import sys
5import SELinuxNeverallowTestFrame
6
7usage = "Usage: ./SELinuxNeverallowTestGen.py <input policy file> <output cts java source>"
8
9
10class NeverallowRule:
11    statement = ''
12    treble_only = False
13
14    def __init__(self, statement):
15        self.statement = statement
16        self.treble_only = False
17
18
19# extract_neverallow_rules - takes an intermediate policy file and pulls out the
20# neverallow rules by taking all of the non-commented text between the 'neverallow'
21# keyword and a terminating ';'
22# returns: a list of rules
23def extract_neverallow_rules(policy_file):
24    with open(policy_file, 'r') as in_file:
25        policy_str = in_file.read()
26
27        # full-Treble only tests are inside sections delimited by BEGIN_TREBLE_ONLY
28        # and END_TREBLE_ONLY comments.
29
30        # uncomment TREBLE_ONLY section delimiter lines
31        remaining = re.sub(
32            r'^\s*#\s*(BEGIN_TREBLE_ONLY|END_TREBLE_ONLY)',
33            r'\1',
34            policy_str,
35            flags = re.M)
36        # remove comments
37        remaining = re.sub(r'#.+?$', r'', remaining, flags = re.M)
38        # match neverallow rules
39        lines = re.findall(
40            r'^\s*(neverallow\s.+?;|BEGIN_TREBLE_ONLY|END_TREBLE_ONLY)',
41            remaining,
42            flags = re.M |re.S)
43
44        # extract neverallow rules from the remaining lines
45        rules = list()
46        treble_only_depth = 0
47        for line in lines:
48            if line.startswith("BEGIN_TREBLE_ONLY"):
49                treble_only_depth += 1
50                continue
51            elif line.startswith("END_TREBLE_ONLY"):
52                if treble_only_depth < 1:
53                    exit("ERROR: END_TREBLE_ONLY outside of TREBLE_ONLY section")
54                treble_only_depth -= 1
55                continue
56            rule = NeverallowRule(line)
57            rule.treble_only = (treble_only_depth > 0)
58            rules.append(rule)
59
60        if treble_only_depth != 0:
61            exit("ERROR: end of input while inside TREBLE_ONLY section")
62        return rules
63
64# neverallow_rule_to_test - takes a neverallow statement and transforms it into
65# the output necessary to form a cts unit test in a java source file.
66# returns: a string representing a generic test method based on this rule.
67def neverallow_rule_to_test(rule, test_num):
68    squashed_neverallow = rule.statement.replace("\n", " ")
69    method  = SELinuxNeverallowTestFrame.src_method
70    method = method.replace("testNeverallowRules()",
71        "testNeverallowRules" + str(test_num) + "()")
72    method = method.replace("$NEVERALLOW_RULE_HERE$", squashed_neverallow)
73    method = method.replace(
74        "$FULL_TREBLE_ONLY_BOOL_HERE$",
75        "true" if rule.treble_only else "false")
76    return method
77
78if __name__ == "__main__":
79    # check usage
80    if len(sys.argv) != 3:
81        print usage
82        exit(1)
83    input_file = sys.argv[1]
84    output_file = sys.argv[2]
85
86    src_header = SELinuxNeverallowTestFrame.src_header
87    src_body = SELinuxNeverallowTestFrame.src_body
88    src_footer = SELinuxNeverallowTestFrame.src_footer
89
90    # grab the neverallow rules from the policy file and transform into tests
91    neverallow_rules = extract_neverallow_rules(input_file)
92    i = 0
93    for rule in neverallow_rules:
94        src_body += neverallow_rule_to_test(rule, i)
95        i += 1
96
97    with open(output_file, 'w') as out_file:
98        out_file.write(src_header)
99        out_file.write(src_body)
100        out_file.write(src_footer)
101