xref: /aosp_15_r20/system/sepolicy/tests/treble_sepolicy_tests.py (revision e4a36f4174b17bbab9dc043f4a65dc8d87377290)
1# Copyright 2021 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15from optparse import OptionParser
16from optparse import Option, OptionValueError
17import os
18import mini_parser
19import re
20import shutil
21import sys
22import tempfile
23
24'''
25Verify that Treble compatibility are not broken.
26'''
27
28
29#############################################################
30# Tests
31#############################################################
32
33###
34# Make sure that any new public type introduced in the new policy that was not
35# present in the old policy has been recorded in the mapping file.
36def TestNoUnmappedNewTypes(base_pub_policy, old_pub_policy, mapping):
37    newt = base_pub_policy.types - old_pub_policy.types
38    ret = ""
39    violators = []
40
41    for n in newt:
42        if mapping.rTypeattributesets.get(n) is None:
43            violators.append(n)
44
45    if len(violators) > 0:
46        ret += "SELinux: The following public types were found added to the "
47        ret += "policy without an entry into the compatibility mapping file(s) "
48        ret += "found in private/compat/V.v/V.v[.ignore].cil, where V.v is the "
49        ret += "latest API level.\n"
50        ret += " ".join(str(x) for x in sorted(violators)) + "\n\n"
51        ret += "See examples of how to fix this:\n"
52        ret += "https://android-review.googlesource.com/c/platform/system/sepolicy/+/781036\n"
53        ret += "https://android-review.googlesource.com/c/platform/system/sepolicy/+/852612\n"
54    return ret
55
56###
57# Make sure that any public type removed in the current policy has its
58# declaration added to the mapping file for use in non-platform policy
59def TestNoUnmappedRmTypes(base_pub_policy, old_pub_policy, mapping):
60    rmt = old_pub_policy.types - base_pub_policy.types
61    ret = ""
62    violators = []
63
64    for o in rmt:
65        if o in mapping.pubtypes and not o in mapping.types:
66            violators.append(o)
67
68    if len(violators) > 0:
69        ret += "SELinux: The following formerly public types were removed from "
70        ret += "policy without a declaration in the compatibility mapping "
71        ret += "found in private/compat/V.v/V.v[.ignore].cil, where V.v is the "
72        ret += "latest API level.\n"
73        ret += " ".join(str(x) for x in sorted(violators)) + "\n\n"
74        ret += "See examples of how to fix this:\n"
75        ret += "https://android-review.googlesource.com/c/platform/system/sepolicy/+/822743\n"
76    return ret
77
78def TestTrebleCompatMapping(base_pub_policy, old_pub_policy, mapping):
79    ret = TestNoUnmappedNewTypes(base_pub_policy, old_pub_policy, mapping)
80    ret += TestNoUnmappedRmTypes(base_pub_policy, old_pub_policy, mapping)
81    return ret
82
83###
84# extend OptionParser to allow the same option flag to be used multiple times.
85# This is used to allow multiple file_contexts files and tests to be
86# specified.
87#
88class MultipleOption(Option):
89    ACTIONS = Option.ACTIONS + ("extend",)
90    STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",)
91    TYPED_ACTIONS = Option.TYPED_ACTIONS + ("extend",)
92    ALWAYS_TYPED_ACTIONS = Option.ALWAYS_TYPED_ACTIONS + ("extend",)
93
94    def take_action(self, action, dest, opt, value, values, parser):
95        if action == "extend":
96            values.ensure_value(dest, []).append(value)
97        else:
98            Option.take_action(self, action, dest, opt, value, values, parser)
99
100def do_main():
101    usage = "treble_sepolicy_tests "
102    usage += "-b base_pub_policy -o old_pub_policy "
103    usage += "-m mapping file [--test test] [--help]"
104    parser = OptionParser(option_class=MultipleOption, usage=usage)
105    parser.add_option("-b", "--base-pub-policy", dest="base_pub_policy",
106                      metavar="FILE")
107    parser.add_option("-m", "--mapping", dest="mapping", metavar="FILE")
108    parser.add_option("-o", "--old-pub-policy", dest="old_pub_policy",
109                      metavar="FILE")
110
111    (options, args) = parser.parse_args()
112
113    # Mapping files and public platform policy are only necessary for the
114    # TrebleCompatMapping test.
115    if not options.mapping:
116        sys.exit("Must specify a compatibility mapping file\n"
117                    + parser.usage)
118    if not options.old_pub_policy:
119        sys.exit("Must specify the previous public policy .cil file\n"
120                    + parser.usage)
121    if not options.base_pub_policy:
122        sys.exit("Must specify the current platform-only public policy "
123                    + ".cil file\n" + parser.usage)
124    mapping = mini_parser.MiniCilParser(options.mapping)
125    base_pub_policy = mini_parser.MiniCilParser(options.base_pub_policy)
126    old_pub_policy = mini_parser.MiniCilParser(options.old_pub_policy)
127
128    results = TestTrebleCompatMapping(base_pub_policy, old_pub_policy, mapping)
129
130    if len(results) > 0:
131        sys.exit(results)
132
133if __name__ == '__main__':
134    do_main()
135