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 }