github.com/westcoastroms/westcoastroms-build@v0.0.0-20190928114312-2350e5a73030/build/soong/android/neverallow.go (about)

     1  // 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  
    15  package android
    16  
    17  import (
    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  
    44  func registerNeverallowMutator(ctx RegisterMutatorsContext) {
    45  	ctx.BottomUp("neverallow", neverallowMutator).Parallel()
    46  }
    47  
    48  var neverallows = []*rule{
    49  	neverallow().
    50  		in("vendor", "device").
    51  		with("vndk.enabled", "true").
    52  		without("vendor", "true").
    53  		because("the VNDK can never contain a library that is device dependent."),
    54  	neverallow().
    55  		with("vndk.enabled", "true").
    56  		without("vendor", "true").
    57  		without("owner", "").
    58  		because("a VNDK module can never have an owner."),
    59  	neverallow().notIn("libcore").with("no_standard_libs", "true"),
    60  
    61  	// TODO(b/67974785): always enforce the manifest
    62  	neverallow().
    63  		without("name", "libhidltransport").
    64  		with("product_variables.enforce_vintf_manifest.cflags", "*").
    65  		because("manifest enforcement should be independent of ."),
    66  
    67  	// TODO(b/67975799): vendor code should always use /vendor/bin/sh
    68  	neverallow().
    69  		without("name", "libc_bionic_ndk").
    70  		with("product_variables.treble_linker_namespaces.cflags", "*").
    71  		because("nothing should care if linker namespaces are enabled or not"),
    72  
    73  	// Example:
    74  	// *neverallow().with("Srcs", "main.cpp"),
    75  }
    76  
    77  func neverallowMutator(ctx BottomUpMutatorContext) {
    78  	m, ok := ctx.Module().(Module)
    79  	if !ok {
    80  		return
    81  	}
    82  
    83  	dir := ctx.ModuleDir() + "/"
    84  	properties := m.GetProperties()
    85  
    86  	for _, n := range neverallows {
    87  		if !n.appliesToPath(dir) {
    88  			continue
    89  		}
    90  
    91  		if !n.appliesToProperties(properties) {
    92  			continue
    93  		}
    94  
    95  		ctx.ModuleErrorf("violates " + n.String())
    96  	}
    97  }
    98  
    99  type ruleProperty struct {
   100  	fields []string // e.x.: Vndk.Enabled
   101  	value  string   // e.x.: true
   102  }
   103  
   104  type rule struct {
   105  	// User string for why this is a thing.
   106  	reason string
   107  
   108  	paths       []string
   109  	unlessPaths []string
   110  
   111  	props       []ruleProperty
   112  	unlessProps []ruleProperty
   113  }
   114  
   115  func neverallow() *rule {
   116  	return &rule{}
   117  }
   118  func (r *rule) in(path ...string) *rule {
   119  	r.paths = append(r.paths, cleanPaths(path)...)
   120  	return r
   121  }
   122  func (r *rule) notIn(path ...string) *rule {
   123  	r.unlessPaths = append(r.unlessPaths, cleanPaths(path)...)
   124  	return r
   125  }
   126  func (r *rule) with(properties, value string) *rule {
   127  	r.props = append(r.props, ruleProperty{
   128  		fields: fieldNamesForProperties(properties),
   129  		value:  value,
   130  	})
   131  	return r
   132  }
   133  func (r *rule) without(properties, value string) *rule {
   134  	r.unlessProps = append(r.unlessProps, ruleProperty{
   135  		fields: fieldNamesForProperties(properties),
   136  		value:  value,
   137  	})
   138  	return r
   139  }
   140  func (r *rule) because(reason string) *rule {
   141  	r.reason = reason
   142  	return r
   143  }
   144  
   145  func (r *rule) String() string {
   146  	s := "neverallow"
   147  	for _, v := range r.paths {
   148  		s += " dir:" + v + "*"
   149  	}
   150  	for _, v := range r.unlessPaths {
   151  		s += " -dir:" + v + "*"
   152  	}
   153  	for _, v := range r.props {
   154  		s += " " + strings.Join(v.fields, ".") + "=" + v.value
   155  	}
   156  	for _, v := range r.unlessProps {
   157  		s += " -" + strings.Join(v.fields, ".") + "=" + v.value
   158  	}
   159  	if len(r.reason) != 0 {
   160  		s += " which is restricted because " + r.reason
   161  	}
   162  	return s
   163  }
   164  
   165  func (r *rule) appliesToPath(dir string) bool {
   166  	includePath := len(r.paths) == 0 || hasAnyPrefix(dir, r.paths)
   167  	excludePath := hasAnyPrefix(dir, r.unlessPaths)
   168  	return includePath && !excludePath
   169  }
   170  
   171  func (r *rule) appliesToProperties(properties []interface{}) bool {
   172  	includeProps := hasAllProperties(properties, r.props)
   173  	excludeProps := hasAnyProperty(properties, r.unlessProps)
   174  	return includeProps && !excludeProps
   175  }
   176  
   177  // assorted utils
   178  
   179  func cleanPaths(paths []string) []string {
   180  	res := make([]string, len(paths))
   181  	for i, v := range paths {
   182  		res[i] = filepath.Clean(v) + "/"
   183  	}
   184  	return res
   185  }
   186  
   187  func fieldNamesForProperties(propertyNames string) []string {
   188  	names := strings.Split(propertyNames, ".")
   189  	for i, v := range names {
   190  		names[i] = proptools.FieldNameForProperty(v)
   191  	}
   192  	return names
   193  }
   194  
   195  func hasAnyPrefix(s string, prefixes []string) bool {
   196  	for _, prefix := range prefixes {
   197  		if strings.HasPrefix(s, prefix) {
   198  			return true
   199  		}
   200  	}
   201  	return false
   202  }
   203  
   204  func hasAnyProperty(properties []interface{}, props []ruleProperty) bool {
   205  	for _, v := range props {
   206  		if hasProperty(properties, v) {
   207  			return true
   208  		}
   209  	}
   210  	return false
   211  }
   212  
   213  func hasAllProperties(properties []interface{}, props []ruleProperty) bool {
   214  	for _, v := range props {
   215  		if !hasProperty(properties, v) {
   216  			return false
   217  		}
   218  	}
   219  	return true
   220  }
   221  
   222  func hasProperty(properties []interface{}, prop ruleProperty) bool {
   223  	for _, propertyStruct := range properties {
   224  		propertiesValue := reflect.ValueOf(propertyStruct).Elem()
   225  		for _, v := range prop.fields {
   226  			if !propertiesValue.IsValid() {
   227  				break
   228  			}
   229  			propertiesValue = propertiesValue.FieldByName(v)
   230  		}
   231  		if !propertiesValue.IsValid() {
   232  			continue
   233  		}
   234  
   235  		check := func(v string) bool {
   236  			return prop.value == "*" || prop.value == v
   237  		}
   238  
   239  		if matchValue(propertiesValue, check) {
   240  			return true
   241  		}
   242  	}
   243  	return false
   244  }
   245  
   246  func matchValue(value reflect.Value, check func(string) bool) bool {
   247  	if !value.IsValid() {
   248  		return false
   249  	}
   250  
   251  	if value.Kind() == reflect.Ptr {
   252  		if value.IsNil() {
   253  			return check("")
   254  		}
   255  		value = value.Elem()
   256  	}
   257  
   258  	switch value.Kind() {
   259  	case reflect.String:
   260  		return check(value.String())
   261  	case reflect.Bool:
   262  		return check(strconv.FormatBool(value.Bool()))
   263  	case reflect.Int:
   264  		return check(strconv.FormatInt(value.Int(), 10))
   265  	case reflect.Slice:
   266  		slice, ok := value.Interface().([]string)
   267  		if !ok {
   268  			panic("Can only handle slice of string")
   269  		}
   270  		for _, v := range slice {
   271  			if check(v) {
   272  				return true
   273  			}
   274  		}
   275  		return false
   276  	}
   277  
   278  	panic("Can't handle type: " + value.Kind().String())
   279  }