blob: ad1d5bd234add2898d740c007a4213d947a61391 [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:
Paul Duffin730f2a52019-06-27 14:08:51 +010034// - 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
Steven Moreland65b3fd92017-12-06 14:18:35 -080037// - - 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
Paul Duffin730f2a52019-06-27 14:08:51 +010042// - it has none of the "Without" properties matched (same rules as above)
Steven Moreland65b3fd92017-12-06 14:18:35 -080043
44func registerNeverallowMutator(ctx RegisterMutatorsContext) {
45 ctx.BottomUp("neverallow", neverallowMutator).Parallel()
46}
47
Paul Duffin730f2a52019-06-27 14:08:51 +010048var neverallows = []Rule{}
Steven Moreland65b3fd92017-12-06 14:18:35 -080049
Paul Duffin730f2a52019-06-27 14:08:51 +010050func init() {
51 AddNeverAllowRules(createTrebleRules()...)
52 AddNeverAllowRules(createLibcoreRules()...)
53 AddNeverAllowRules(createMediaRules()...)
54 AddNeverAllowRules(createJavaDeviceForHostRules()...)
Neil Fullerdf5f3562018-10-21 17:19:10 +010055}
Steven Moreland65b3fd92017-12-06 14:18:35 -080056
Paul Duffin730f2a52019-06-27 14:08:51 +010057// Add a NeverAllow rule to the set of rules to apply.
58func AddNeverAllowRules(rules ...Rule) {
59 neverallows = append(neverallows, rules...)
60}
61
62func createTrebleRules() []Rule {
63 return []Rule{
64 NeverAllow().
65 In("vendor", "device").
66 With("vndk.enabled", "true").
67 Without("vendor", "true").
68 Because("the VNDK can never contain a library that is device dependent."),
69 NeverAllow().
70 With("vndk.enabled", "true").
71 Without("vendor", "true").
72 Without("owner", "").
73 Because("a VNDK module can never have an owner."),
Steven Moreland65b3fd92017-12-06 14:18:35 -080074
Neil Fullerdf5f3562018-10-21 17:19:10 +010075 // TODO(b/67974785): always enforce the manifest
Paul Duffin730f2a52019-06-27 14:08:51 +010076 NeverAllow().
77 Without("name", "libhidltransport-impl-internal").
78 With("product_variables.enforce_vintf_manifest.cflags", "*").
79 Because("manifest enforcement should be independent of ."),
Neil Fullerdf5f3562018-10-21 17:19:10 +010080
81 // TODO(b/67975799): vendor code should always use /vendor/bin/sh
Paul Duffin730f2a52019-06-27 14:08:51 +010082 NeverAllow().
83 Without("name", "libc_bionic_ndk").
84 With("product_variables.treble_linker_namespaces.cflags", "*").
85 Because("nothing should care if linker namespaces are enabled or not"),
Neil Fullerdf5f3562018-10-21 17:19:10 +010086
87 // Example:
Paul Duffin730f2a52019-06-27 14:08:51 +010088 // *NeverAllow().with("Srcs", "main.cpp"))
Neil Fullerdf5f3562018-10-21 17:19:10 +010089 }
90}
91
Paul Duffin730f2a52019-06-27 14:08:51 +010092func createLibcoreRules() []Rule {
Neil Fullerdf5f3562018-10-21 17:19:10 +010093 var coreLibraryProjects = []string{
94 "libcore",
95 "external/apache-harmony",
96 "external/apache-xml",
97 "external/bouncycastle",
98 "external/conscrypt",
99 "external/icu",
100 "external/okhttp",
101 "external/wycheproof",
Paul Duffinb6c6bdd2019-06-07 11:43:55 +0100102
103 // Not really a core library but still needs access to same capabilities.
104 "development",
Neil Fullerdf5f3562018-10-21 17:19:10 +0100105 }
106
Paul Duffina3d09862019-06-11 13:40:47 +0100107 // Core library constraints. The sdk_version: "none" can only be used in core library projects.
108 // Access to core library targets is restricted using visibility rules.
Paul Duffin730f2a52019-06-27 14:08:51 +0100109 rules := []Rule{
110 NeverAllow().
111 NotIn(coreLibraryProjects...).
112 With("sdk_version", "none"),
Neil Fullerdf5f3562018-10-21 17:19:10 +0100113 }
114
Neil Fullerdf5f3562018-10-21 17:19:10 +0100115 return rules
Steven Moreland65b3fd92017-12-06 14:18:35 -0800116}
117
Paul Duffin730f2a52019-06-27 14:08:51 +0100118func createMediaRules() []Rule {
119 return []Rule{
120 NeverAllow().
121 With("libs", "updatable-media").
122 Because("updatable-media includes private APIs. Use updatable_media_stubs instead."),
Dongwon Kang50a299f2019-02-04 09:00:51 -0800123 }
124}
125
Paul Duffin730f2a52019-06-27 14:08:51 +0100126func createJavaDeviceForHostRules() []Rule {
Colin Crossfd4f7432019-03-05 15:06:16 -0800127 javaDeviceForHostProjectsWhitelist := []string{
Colin Crossb5191a52019-04-11 14:07:38 -0700128 "external/guava",
Colin Crossfd4f7432019-03-05 15:06:16 -0800129 "external/robolectric-shadows",
130 "framework/layoutlib",
131 }
132
Paul Duffin730f2a52019-06-27 14:08:51 +0100133 return []Rule{
134 NeverAllow().
135 NotIn(javaDeviceForHostProjectsWhitelist...).
136 ModuleType("java_device_for_host", "java_host_for_device").
137 Because("java_device_for_host can only be used in whitelisted projects"),
Colin Crossfd4f7432019-03-05 15:06:16 -0800138 }
139}
140
Steven Moreland65b3fd92017-12-06 14:18:35 -0800141func neverallowMutator(ctx BottomUpMutatorContext) {
142 m, ok := ctx.Module().(Module)
143 if !ok {
144 return
145 }
146
147 dir := ctx.ModuleDir() + "/"
148 properties := m.GetProperties()
149
Paul Duffin730f2a52019-06-27 14:08:51 +0100150 for _, r := range neverallows {
151 n := r.(*rule)
Steven Moreland65b3fd92017-12-06 14:18:35 -0800152 if !n.appliesToPath(dir) {
153 continue
154 }
155
Colin Crossfd4f7432019-03-05 15:06:16 -0800156 if !n.appliesToModuleType(ctx.ModuleType()) {
157 continue
158 }
159
Steven Moreland65b3fd92017-12-06 14:18:35 -0800160 if !n.appliesToProperties(properties) {
161 continue
162 }
163
164 ctx.ModuleErrorf("violates " + n.String())
165 }
166}
167
Paul Duffin73bf0542019-07-12 14:12:49 +0100168type ValueMatcher interface {
169 test(string) bool
170 String() string
171}
172
173type equalMatcher struct {
174 expected string
175}
176
177func (m *equalMatcher) test(value string) bool {
178 return m.expected == value
179}
180
181func (m *equalMatcher) String() string {
182 return "=" + m.expected
183}
184
185type anyMatcher struct {
186}
187
188func (m *anyMatcher) test(value string) bool {
189 return true
190}
191
192func (m *anyMatcher) String() string {
193 return "=*"
194}
195
196var anyMatcherInstance = &anyMatcher{}
197
Steven Moreland65b3fd92017-12-06 14:18:35 -0800198type ruleProperty struct {
Paul Duffin73bf0542019-07-12 14:12:49 +0100199 fields []string // e.x.: Vndk.Enabled
200 matcher ValueMatcher
Steven Moreland65b3fd92017-12-06 14:18:35 -0800201}
202
Paul Duffin730f2a52019-06-27 14:08:51 +0100203// A NeverAllow rule.
204type Rule interface {
205 In(path ...string) Rule
206
207 NotIn(path ...string) Rule
208
209 ModuleType(types ...string) Rule
210
211 NotModuleType(types ...string) Rule
212
213 With(properties, value string) Rule
214
215 Without(properties, value string) Rule
216
217 Because(reason string) Rule
218}
219
Steven Moreland65b3fd92017-12-06 14:18:35 -0800220type rule struct {
221 // User string for why this is a thing.
222 reason string
223
224 paths []string
225 unlessPaths []string
226
Colin Crossfd4f7432019-03-05 15:06:16 -0800227 moduleTypes []string
228 unlessModuleTypes []string
229
Steven Moreland65b3fd92017-12-06 14:18:35 -0800230 props []ruleProperty
231 unlessProps []ruleProperty
232}
233
Paul Duffin730f2a52019-06-27 14:08:51 +0100234// Create a new NeverAllow rule.
235func NeverAllow() Rule {
Steven Moreland65b3fd92017-12-06 14:18:35 -0800236 return &rule{}
237}
Colin Crossfd4f7432019-03-05 15:06:16 -0800238
Paul Duffin730f2a52019-06-27 14:08:51 +0100239func (r *rule) In(path ...string) Rule {
Steven Moreland65b3fd92017-12-06 14:18:35 -0800240 r.paths = append(r.paths, cleanPaths(path)...)
241 return r
242}
Colin Crossfd4f7432019-03-05 15:06:16 -0800243
Paul Duffin730f2a52019-06-27 14:08:51 +0100244func (r *rule) NotIn(path ...string) Rule {
Steven Moreland65b3fd92017-12-06 14:18:35 -0800245 r.unlessPaths = append(r.unlessPaths, cleanPaths(path)...)
246 return r
247}
Colin Crossfd4f7432019-03-05 15:06:16 -0800248
Paul Duffin730f2a52019-06-27 14:08:51 +0100249func (r *rule) ModuleType(types ...string) Rule {
Colin Crossfd4f7432019-03-05 15:06:16 -0800250 r.moduleTypes = append(r.moduleTypes, types...)
251 return r
252}
253
Paul Duffin730f2a52019-06-27 14:08:51 +0100254func (r *rule) NotModuleType(types ...string) Rule {
Colin Crossfd4f7432019-03-05 15:06:16 -0800255 r.unlessModuleTypes = append(r.unlessModuleTypes, types...)
256 return r
257}
258
Paul Duffin730f2a52019-06-27 14:08:51 +0100259func (r *rule) With(properties, value string) Rule {
Steven Moreland65b3fd92017-12-06 14:18:35 -0800260 r.props = append(r.props, ruleProperty{
Paul Duffin73bf0542019-07-12 14:12:49 +0100261 fields: fieldNamesForProperties(properties),
Ming-Shin Luff3d72f2019-07-22 06:44:34 +0000262 matcher: selectMatcher(value),
Steven Moreland65b3fd92017-12-06 14:18:35 -0800263 })
264 return r
265}
Colin Crossfd4f7432019-03-05 15:06:16 -0800266
Paul Duffin730f2a52019-06-27 14:08:51 +0100267func (r *rule) Without(properties, value string) Rule {
Steven Moreland65b3fd92017-12-06 14:18:35 -0800268 r.unlessProps = append(r.unlessProps, ruleProperty{
Paul Duffin73bf0542019-07-12 14:12:49 +0100269 fields: fieldNamesForProperties(properties),
Ming-Shin Luff3d72f2019-07-22 06:44:34 +0000270 matcher: selectMatcher(value),
Steven Moreland65b3fd92017-12-06 14:18:35 -0800271 })
272 return r
273}
Colin Crossfd4f7432019-03-05 15:06:16 -0800274
Paul Duffin73bf0542019-07-12 14:12:49 +0100275func selectMatcher(expected string) ValueMatcher {
276 if expected == "*" {
277 return anyMatcherInstance
278 }
279 return &equalMatcher{expected: expected}
280}
281
Paul Duffin730f2a52019-06-27 14:08:51 +0100282func (r *rule) Because(reason string) Rule {
Steven Moreland65b3fd92017-12-06 14:18:35 -0800283 r.reason = reason
284 return r
285}
286
287func (r *rule) String() string {
288 s := "neverallow"
289 for _, v := range r.paths {
290 s += " dir:" + v + "*"
291 }
292 for _, v := range r.unlessPaths {
293 s += " -dir:" + v + "*"
294 }
Colin Crossfd4f7432019-03-05 15:06:16 -0800295 for _, v := range r.moduleTypes {
296 s += " type:" + v
297 }
298 for _, v := range r.unlessModuleTypes {
299 s += " -type:" + v
300 }
Steven Moreland65b3fd92017-12-06 14:18:35 -0800301 for _, v := range r.props {
Paul Duffin73bf0542019-07-12 14:12:49 +0100302 s += " " + strings.Join(v.fields, ".") + v.matcher.String()
Steven Moreland65b3fd92017-12-06 14:18:35 -0800303 }
304 for _, v := range r.unlessProps {
Paul Duffin73bf0542019-07-12 14:12:49 +0100305 s += " -" + strings.Join(v.fields, ".") + v.matcher.String()
Steven Moreland65b3fd92017-12-06 14:18:35 -0800306 }
307 if len(r.reason) != 0 {
308 s += " which is restricted because " + r.reason
309 }
310 return s
311}
312
313func (r *rule) appliesToPath(dir string) bool {
314 includePath := len(r.paths) == 0 || hasAnyPrefix(dir, r.paths)
315 excludePath := hasAnyPrefix(dir, r.unlessPaths)
316 return includePath && !excludePath
317}
318
Colin Crossfd4f7432019-03-05 15:06:16 -0800319func (r *rule) appliesToModuleType(moduleType string) bool {
320 return (len(r.moduleTypes) == 0 || InList(moduleType, r.moduleTypes)) && !InList(moduleType, r.unlessModuleTypes)
321}
322
Steven Moreland65b3fd92017-12-06 14:18:35 -0800323func (r *rule) appliesToProperties(properties []interface{}) bool {
324 includeProps := hasAllProperties(properties, r.props)
325 excludeProps := hasAnyProperty(properties, r.unlessProps)
326 return includeProps && !excludeProps
327}
328
329// assorted utils
330
331func cleanPaths(paths []string) []string {
332 res := make([]string, len(paths))
333 for i, v := range paths {
334 res[i] = filepath.Clean(v) + "/"
335 }
336 return res
337}
338
339func fieldNamesForProperties(propertyNames string) []string {
340 names := strings.Split(propertyNames, ".")
341 for i, v := range names {
342 names[i] = proptools.FieldNameForProperty(v)
343 }
344 return names
345}
346
347func hasAnyPrefix(s string, prefixes []string) bool {
348 for _, prefix := range prefixes {
349 if strings.HasPrefix(s, prefix) {
350 return true
351 }
352 }
353 return false
354}
355
356func hasAnyProperty(properties []interface{}, props []ruleProperty) bool {
357 for _, v := range props {
358 if hasProperty(properties, v) {
359 return true
360 }
361 }
362 return false
363}
364
365func hasAllProperties(properties []interface{}, props []ruleProperty) bool {
366 for _, v := range props {
367 if !hasProperty(properties, v) {
368 return false
369 }
370 }
371 return true
372}
373
374func hasProperty(properties []interface{}, prop ruleProperty) bool {
375 for _, propertyStruct := range properties {
376 propertiesValue := reflect.ValueOf(propertyStruct).Elem()
377 for _, v := range prop.fields {
378 if !propertiesValue.IsValid() {
379 break
380 }
381 propertiesValue = propertiesValue.FieldByName(v)
382 }
383 if !propertiesValue.IsValid() {
384 continue
385 }
386
Paul Duffin73bf0542019-07-12 14:12:49 +0100387 check := func(value string) bool {
388 return prop.matcher.test(value)
Steven Moreland65b3fd92017-12-06 14:18:35 -0800389 }
390
391 if matchValue(propertiesValue, check) {
392 return true
393 }
394 }
395 return false
396}
397
398func matchValue(value reflect.Value, check func(string) bool) bool {
399 if !value.IsValid() {
400 return false
401 }
402
403 if value.Kind() == reflect.Ptr {
404 if value.IsNil() {
405 return check("")
406 }
407 value = value.Elem()
408 }
409
410 switch value.Kind() {
411 case reflect.String:
412 return check(value.String())
413 case reflect.Bool:
414 return check(strconv.FormatBool(value.Bool()))
415 case reflect.Int:
416 return check(strconv.FormatInt(value.Int(), 10))
417 case reflect.Slice:
418 slice, ok := value.Interface().([]string)
419 if !ok {
420 panic("Can only handle slice of string")
421 }
422 for _, v := range slice {
423 if check(v) {
424 return true
425 }
426 }
427 return false
428 }
429
430 panic("Can't handle type: " + value.Kind().String())
431}