blob: d3a28010078d5ceaf8c0e9d1d92f0581005dcdc7 [file] [log] [blame]
Steven Moreland65b3fd92017-12-06 14:18:35 -08001// Copyright 2017 Google Inc. All rights reserved.
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
15package android
16
17import (
18 "path/filepath"
19 "reflect"
20 "strconv"
21 "strings"
22
23 "github.com/google/blueprint/proptools"
24)
25
26// "neverallow" rules for the build system.
27//
28// This allows things which aren't related to the build system and are enforced
29// for sanity, in progress code refactors, or policy to be expressed in a
30// straightforward away disjoint from implementations and tests which should
31// work regardless of these restrictions.
32//
33// A module is disallowed if all of the following are true:
34// - it is in one of the "in" paths
35// - it is not in one of the "notIn" paths
36// - it has all "with" properties matched
37// - - values are matched in their entirety
38// - - nil is interpreted as an empty string
39// - - nested properties are separated with a '.'
40// - - if the property is a list, any of the values in the list being matches
41// counts as a match
42// - it has none of the "without" properties matched (same rules as above)
43
44func registerNeverallowMutator(ctx RegisterMutatorsContext) {
45 ctx.BottomUp("neverallow", neverallowMutator).Parallel()
46}
47
48var neverallows = []*rule{
Logan Chienaf29bad2018-03-12 16:35:58 +080049 neverallow().
50 in("vendor", "device").
51 with("vndk.enabled", "true").
52 without("vendor", "true").
Steven Moreland65b3fd92017-12-06 14:18:35 -080053 because("the VNDK can never contain a library that is device dependent."),
Logan Chienaf29bad2018-03-12 16:35:58 +080054 neverallow().
55 with("vndk.enabled", "true").
56 without("vendor", "true").
57 without("owner", "").
Steven Moreland65b3fd92017-12-06 14:18:35 -080058 because("a VNDK module can never have an owner."),
Neil Fullerfe385b02018-10-11 09:52:42 +010059 neverallow().
60 notIn("libcore", "development", "external/apache-harmony", "external/apache-xml", "external/bouncycastle", "external/conscrypt", "external/icu", "external/okhttp", "external/wycheproof").
61 with("no_standard_libs", "true"),
Steven Moreland65b3fd92017-12-06 14:18:35 -080062
63 // TODO(b/67974785): always enforce the manifest
64 neverallow().
65 without("name", "libhidltransport").
66 with("product_variables.enforce_vintf_manifest.cflags", "*").
67 because("manifest enforcement should be independent of ."),
68
69 // TODO(b/67975799): vendor code should always use /vendor/bin/sh
70 neverallow().
71 without("name", "libc_bionic_ndk").
72 with("product_variables.treble_linker_namespaces.cflags", "*").
73 because("nothing should care if linker namespaces are enabled or not"),
74
75 // Example:
76 // *neverallow().with("Srcs", "main.cpp"),
77}
78
79func neverallowMutator(ctx BottomUpMutatorContext) {
80 m, ok := ctx.Module().(Module)
81 if !ok {
82 return
83 }
84
85 dir := ctx.ModuleDir() + "/"
86 properties := m.GetProperties()
87
88 for _, n := range neverallows {
89 if !n.appliesToPath(dir) {
90 continue
91 }
92
93 if !n.appliesToProperties(properties) {
94 continue
95 }
96
97 ctx.ModuleErrorf("violates " + n.String())
98 }
99}
100
101type ruleProperty struct {
102 fields []string // e.x.: Vndk.Enabled
103 value string // e.x.: true
104}
105
106type rule struct {
107 // User string for why this is a thing.
108 reason string
109
110 paths []string
111 unlessPaths []string
112
113 props []ruleProperty
114 unlessProps []ruleProperty
115}
116
117func neverallow() *rule {
118 return &rule{}
119}
120func (r *rule) in(path ...string) *rule {
121 r.paths = append(r.paths, cleanPaths(path)...)
122 return r
123}
124func (r *rule) notIn(path ...string) *rule {
125 r.unlessPaths = append(r.unlessPaths, cleanPaths(path)...)
126 return r
127}
128func (r *rule) with(properties, value string) *rule {
129 r.props = append(r.props, ruleProperty{
130 fields: fieldNamesForProperties(properties),
131 value: value,
132 })
133 return r
134}
135func (r *rule) without(properties, value string) *rule {
136 r.unlessProps = append(r.unlessProps, ruleProperty{
137 fields: fieldNamesForProperties(properties),
138 value: value,
139 })
140 return r
141}
142func (r *rule) because(reason string) *rule {
143 r.reason = reason
144 return r
145}
146
147func (r *rule) String() string {
148 s := "neverallow"
149 for _, v := range r.paths {
150 s += " dir:" + v + "*"
151 }
152 for _, v := range r.unlessPaths {
153 s += " -dir:" + v + "*"
154 }
155 for _, v := range r.props {
156 s += " " + strings.Join(v.fields, ".") + "=" + v.value
157 }
158 for _, v := range r.unlessProps {
159 s += " -" + strings.Join(v.fields, ".") + "=" + v.value
160 }
161 if len(r.reason) != 0 {
162 s += " which is restricted because " + r.reason
163 }
164 return s
165}
166
167func (r *rule) appliesToPath(dir string) bool {
168 includePath := len(r.paths) == 0 || hasAnyPrefix(dir, r.paths)
169 excludePath := hasAnyPrefix(dir, r.unlessPaths)
170 return includePath && !excludePath
171}
172
173func (r *rule) appliesToProperties(properties []interface{}) bool {
174 includeProps := hasAllProperties(properties, r.props)
175 excludeProps := hasAnyProperty(properties, r.unlessProps)
176 return includeProps && !excludeProps
177}
178
179// assorted utils
180
181func cleanPaths(paths []string) []string {
182 res := make([]string, len(paths))
183 for i, v := range paths {
184 res[i] = filepath.Clean(v) + "/"
185 }
186 return res
187}
188
189func fieldNamesForProperties(propertyNames string) []string {
190 names := strings.Split(propertyNames, ".")
191 for i, v := range names {
192 names[i] = proptools.FieldNameForProperty(v)
193 }
194 return names
195}
196
197func hasAnyPrefix(s string, prefixes []string) bool {
198 for _, prefix := range prefixes {
199 if strings.HasPrefix(s, prefix) {
200 return true
201 }
202 }
203 return false
204}
205
206func hasAnyProperty(properties []interface{}, props []ruleProperty) bool {
207 for _, v := range props {
208 if hasProperty(properties, v) {
209 return true
210 }
211 }
212 return false
213}
214
215func hasAllProperties(properties []interface{}, props []ruleProperty) bool {
216 for _, v := range props {
217 if !hasProperty(properties, v) {
218 return false
219 }
220 }
221 return true
222}
223
224func hasProperty(properties []interface{}, prop ruleProperty) bool {
225 for _, propertyStruct := range properties {
226 propertiesValue := reflect.ValueOf(propertyStruct).Elem()
227 for _, v := range prop.fields {
228 if !propertiesValue.IsValid() {
229 break
230 }
231 propertiesValue = propertiesValue.FieldByName(v)
232 }
233 if !propertiesValue.IsValid() {
234 continue
235 }
236
237 check := func(v string) bool {
238 return prop.value == "*" || prop.value == v
239 }
240
241 if matchValue(propertiesValue, check) {
242 return true
243 }
244 }
245 return false
246}
247
248func matchValue(value reflect.Value, check func(string) bool) bool {
249 if !value.IsValid() {
250 return false
251 }
252
253 if value.Kind() == reflect.Ptr {
254 if value.IsNil() {
255 return check("")
256 }
257 value = value.Elem()
258 }
259
260 switch value.Kind() {
261 case reflect.String:
262 return check(value.String())
263 case reflect.Bool:
264 return check(strconv.FormatBool(value.Bool()))
265 case reflect.Int:
266 return check(strconv.FormatInt(value.Int(), 10))
267 case reflect.Slice:
268 slice, ok := value.Interface().([]string)
269 if !ok {
270 panic("Can only handle slice of string")
271 }
272 for _, v := range slice {
273 if check(v) {
274 return true
275 }
276 }
277 return false
278 }
279
280 panic("Can't handle type: " + value.Kind().String())
281}