• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
15package android
16
17import (
18	"fmt"
19	"path/filepath"
20	"reflect"
21	"regexp"
22	"strconv"
23	"strings"
24
25	"github.com/google/blueprint/proptools"
26)
27
28// "neverallow" rules for the build system.
29//
30// This allows things which aren't related to the build system and are enforced
31// against assumptions, in progress code refactors, or policy to be expressed in a
32// straightforward away disjoint from implementations and tests which should
33// work regardless of these restrictions.
34//
35// A module is disallowed if all of the following are true:
36// - it is in one of the "In" paths
37// - it is not in one of the "NotIn" paths
38// - it has all "With" properties matched
39// - - values are matched in their entirety
40// - - nil is interpreted as an empty string
41// - - nested properties are separated with a '.'
42// - - if the property is a list, any of the values in the list being matches
43//     counts as a match
44// - it has none of the "Without" properties matched (same rules as above)
45
46func registerNeverallowMutator(ctx RegisterMutatorsContext) {
47	ctx.BottomUp("neverallow", neverallowMutator)
48}
49
50var neverallows = []Rule{}
51
52func init() {
53	AddNeverAllowRules(createIncludeDirsRules()...)
54	AddNeverAllowRules(createTrebleRules()...)
55	AddNeverAllowRules(createJavaDeviceForHostRules()...)
56	AddNeverAllowRules(createCcSdkVariantRules()...)
57	AddNeverAllowRules(createUncompressDexRules()...)
58	AddNeverAllowRules(createInstallInRootAllowingRules()...)
59	AddNeverAllowRules(createProhibitFrameworkAccessRules()...)
60	AddNeverAllowRules(createCcStubsRule())
61	AddNeverAllowRules(createProhibitHeaderOnlyRule())
62	AddNeverAllowRules(createLimitNdkExportRule()...)
63	AddNeverAllowRules(createLimitDirgroupRules()...)
64	AddNeverAllowRules(createLimitGenruleRules()...)
65	AddNeverAllowRules(createFilesystemIsAutoGeneratedRule())
66	AddNeverAllowRules(createKotlinPluginRule()...)
67	AddNeverAllowRules(createPrebuiltEtcBpDefineRule())
68	AddNeverAllowRules(createAutogenRroBpDefineRule())
69	AddNeverAllowRules(createNoSha1HashRule())
70}
71
72// Add a NeverAllow rule to the set of rules to apply.
73func AddNeverAllowRules(rules ...Rule) {
74	neverallows = append(neverallows, rules...)
75}
76
77var (
78	neverallowNotInIncludeDir = []string{
79		"art",
80		"art/libnativebridge",
81		"art/libnativeloader",
82		"libcore",
83		"libnativehelper",
84		"external/apache-harmony",
85		"external/apache-xml",
86		"external/boringssl",
87		"external/bouncycastle",
88		"external/conscrypt",
89		"external/icu",
90		"external/okhttp",
91		"external/vixl",
92		"external/wycheproof",
93	}
94	neverallowNoUseIncludeDir = []string{
95		"frameworks/av/apex",
96		"frameworks/av/tools",
97		"frameworks/native/cmds",
98		"system/apex",
99		"system/bpf",
100		"system/gatekeeper",
101		"system/hwservicemanager",
102		"system/libbase",
103		"system/libfmq",
104		"system/libvintf",
105	}
106)
107
108func createIncludeDirsRules() []Rule {
109	rules := make([]Rule, 0, len(neverallowNotInIncludeDir)+len(neverallowNoUseIncludeDir))
110
111	for _, path := range neverallowNotInIncludeDir {
112		rule :=
113			NeverAllow().
114				WithMatcher("include_dirs", StartsWith(path+"/")).
115				Because("include_dirs is deprecated, all usages of '" + path + "' have been migrated" +
116					" to use alternate mechanisms and so can no longer be used.")
117
118		rules = append(rules, rule)
119	}
120
121	for _, path := range neverallowNoUseIncludeDir {
122		rule := NeverAllow().In(path+"/").WithMatcher("include_dirs", isSetMatcherInstance).
123			Because("include_dirs is deprecated, all usages of them in '" + path + "' have been migrated" +
124				" to use alternate mechanisms and so can no longer be used.")
125		rules = append(rules, rule)
126	}
127
128	return rules
129}
130
131func createTrebleRules() []Rule {
132	return []Rule{
133		NeverAllow().
134			In("vendor", "device").
135			With("vndk.enabled", "true").
136			Without("vendor", "true").
137			Without("product_specific", "true").
138			Because("the VNDK can never contain a library that is device dependent."),
139		NeverAllow().
140			With("vndk.enabled", "true").
141			Without("vendor", "true").
142			Without("owner", "").
143			Because("a VNDK module can never have an owner."),
144
145		// TODO(b/67974785): always enforce the manifest
146		NeverAllow().
147			Without("name", "libhidlbase-combined-impl").
148			Without("name", "libhidlbase").
149			With("product_variables.enforce_vintf_manifest.cflags", "*").
150			Because("manifest enforcement should be independent of ."),
151
152		// TODO(b/67975799): vendor code should always use /vendor/bin/sh
153		NeverAllow().
154			Without("name", "libc_bionic_ndk").
155			With("product_variables.treble_linker_namespaces.cflags", "*").
156			Because("nothing should care if linker namespaces are enabled or not"),
157
158		// Example:
159		// *NeverAllow().with("Srcs", "main.cpp"))
160	}
161}
162
163func createJavaDeviceForHostRules() []Rule {
164	javaDeviceForHostProjectsAllowedList := []string{
165		"development/build",
166		"external/guava",
167		"external/kotlinx.coroutines",
168		"external/robolectric-shadows",
169		"external/robolectric",
170		"frameworks/base/ravenwood",
171		"frameworks/base/tools/hoststubgen",
172		"frameworks/layoutlib",
173	}
174
175	return []Rule{
176		NeverAllow().
177			NotIn(javaDeviceForHostProjectsAllowedList...).
178			ModuleType("java_device_for_host", "java_host_for_device").
179			Because("java_device_for_host can only be used in allowed projects"),
180	}
181}
182
183func createCcSdkVariantRules() []Rule {
184	sdkVersionOnlyAllowedList := []string{
185		// derive_sdk_prefer32 has stem: "derive_sdk" which conflicts with the derive_sdk.
186		// This sometimes works because the APEX modules that contain derive_sdk and
187		// derive_sdk_prefer32 suppress the platform installation rules, but fails when
188		// the APEX modules contain the SDK variant and the platform variant still exists.
189		"packages/modules/SdkExtensions/derive_sdk",
190		// These are for apps and shouldn't be used by non-SDK variant modules.
191		"prebuilts/ndk",
192		"frameworks/native/libs/binder/ndk",
193		"tools/test/graphicsbenchmark/apps/sample_app",
194		"tools/test/graphicsbenchmark/functional_tests/java",
195		"vendor/xts/gts-tests/hostsidetests/gamedevicecert/apps/javatests",
196		"external/libtextclassifier/native",
197	}
198
199	platformVariantPropertiesAllowedList := []string{
200		// android_native_app_glue and libRSSupport use native_window.h but target old
201		// sdk versions (minimum and 9 respectively) where libnativewindow didn't exist,
202		// so they can't add libnativewindow to shared_libs to get the header directory
203		// for the platform variant.  Allow them to use the platform variant
204		// property to set shared_libs.
205		"prebuilts/ndk",
206		"frameworks/rs",
207	}
208
209	return []Rule{
210		NeverAllow().
211			NotIn(sdkVersionOnlyAllowedList...).
212			WithMatcher("sdk_variant_only", isSetMatcherInstance).
213			Because("sdk_variant_only can only be used in allowed projects"),
214		NeverAllow().
215			NotIn(platformVariantPropertiesAllowedList...).
216			WithMatcher("platform.shared_libs", isSetMatcherInstance).
217			Because("platform variant properties can only be used in allowed projects"),
218	}
219}
220
221func createCcStubsRule() Rule {
222	ccStubsImplementationInstallableProjectsAllowedList := []string{
223		"packages/modules/Virtualization/libs/libvm_payload",
224	}
225
226	return NeverAllow().
227		NotIn(ccStubsImplementationInstallableProjectsAllowedList...).
228		WithMatcher("stubs.implementation_installable", isSetMatcherInstance).
229		Because("implementation_installable can only be used in allowed projects.")
230}
231
232func createUncompressDexRules() []Rule {
233	return []Rule{
234		NeverAllow().
235			NotIn("art", "cts/hostsidetests/compilation").
236			WithMatcher("uncompress_dex", isSetMatcherInstance).
237			Because("uncompress_dex is only allowed for certain jars for test in art."),
238	}
239}
240
241func createInstallInRootAllowingRules() []Rule {
242	return []Rule{
243		NeverAllow().
244			Without("name", "init_first_stage_defaults").
245			Without("name", "init_first_stage").
246			Without("name", "init_first_stage.microdroid").
247			Without("name", "librecovery_ui_ext").
248			With("install_in_root", "true").
249			NotModuleType("prebuilt_root").
250			NotModuleType("prebuilt_vendor").
251			NotModuleType("prebuilt_sbin").
252			NotModuleType("prebuilt_system").
253			NotModuleType("prebuilt_first_stage_ramdisk").
254			NotModuleType("prebuilt_res").
255			NotModuleType("prebuilt_any").
256			Because("install_in_root is only for init_first_stage or librecovery_ui_ext."),
257	}
258}
259
260func createProhibitFrameworkAccessRules() []Rule {
261	return []Rule{
262		NeverAllow().
263			With("libs", "framework").
264			WithoutMatcher("sdk_version", Regexp("(core_.*|^$)")).
265			Because("framework can't be used when building against SDK"),
266	}
267}
268
269func createProhibitHeaderOnlyRule() Rule {
270	return NeverAllow().
271		Without("name", "framework-minus-apex-headers").
272		With("headers_only", "true").
273		Because("headers_only can only be used for generating framework-minus-apex headers for non-updatable modules")
274}
275
276func createLimitNdkExportRule() []Rule {
277	reason := "If the headers you're trying to export are meant to be a part of the NDK, they should be exposed by an ndk_headers module. If the headers shouldn't be a part of the NDK, the headers should instead be exposed from a separate `cc_library_headers` which consumers depend on."
278	// DO NOT ADD HERE - please consult danalbert@
279	// b/357711733
280	return []Rule{
281		NeverAllow().
282			NotIn("frameworks/native/libs/binder/ndk").
283			ModuleType("ndk_library").
284			WithMatcher("export_header_libs", isSetMatcherInstance).Because(reason),
285		NeverAllow().ModuleType("ndk_library").WithMatcher("export_generated_headers", isSetMatcherInstance).Because(reason),
286		NeverAllow().ModuleType("ndk_library").WithMatcher("export_include_dirs", isSetMatcherInstance).Because(reason),
287		NeverAllow().ModuleType("ndk_library").WithMatcher("export_shared_lib_headers", isSetMatcherInstance).Because(reason),
288		NeverAllow().ModuleType("ndk_library").WithMatcher("export_static_lib_headers", isSetMatcherInstance).Because(reason),
289	}
290}
291
292func createLimitDirgroupRules() []Rule {
293	reason := "The dirgroup module can only be used with Trusty visibility"
294	scriptsDirsList := []string{"//trusty/vendor/google/aosp/scripts", "//trusty/vendor/google/proprietary/scripts"}
295	return []Rule{
296		NeverAllow().
297			ModuleType("dirgroup").
298			WithMatcher("visibility", NotInList(scriptsDirsList)).Because(reason),
299		NeverAllow().
300			ModuleType("dirgroup").
301			WithoutMatcher("visibility", InAllowedList(scriptsDirsList)).Because(reason),
302	}
303}
304
305func createLimitGenruleRules() []Rule {
306	dirSrcsReason := "The `dir_srcs` property in a `genrule` module can only be used by Trusty"
307	keepGendirReason := "The `keep_gendir` property in a `genrule` module can only be used by Trusty"
308	allowedModuleNameList := []string{
309		// Trusty TEE target names
310		"trusty_tee_package_goog",
311		"trusty_tee_package",
312		// Trusty vm target names
313		"trusty_desktop_vm_arm64.bin",
314		"trusty_desktop_vm_x86_64.bin",
315		"trusty_desktop_test_vm_arm64.bin",
316		"trusty_desktop_test_vm_x86_64.bin",
317		"trusty_test_vm_arm64.bin",
318		"trusty_test_vm_x86_64.elf",
319		"trusty_test_vm_os_arm64.bin",
320		"trusty_test_vm_os_x86_64.elf",
321		"trusty_security_vm_arm64.bin",
322		"trusty_security_vm_x86_64.elf",
323		"trusty_widevine_vm_arm64.bin",
324		"trusty_widevine_vm_x86_64.elf",
325	}
326	return []Rule{
327		NeverAllow().
328			ModuleType("genrule").
329			WithoutMatcher("name", InAllowedList(allowedModuleNameList)).
330			WithMatcher("dir_srcs", isSetMatcherInstance).Because(dirSrcsReason),
331		NeverAllow().
332			ModuleType("genrule").
333			WithoutMatcher("name", InAllowedList(allowedModuleNameList)).
334			With("keep_gendir", "true").Because(keepGendirReason),
335	}
336}
337
338func createFilesystemIsAutoGeneratedRule() Rule {
339	return NeverAllow().
340		NotIn("build/soong/fsgen").
341		ModuleType("filesystem", "android_system_image").
342		WithMatcher("is_auto_generated", isSetMatcherInstance).
343		Because("is_auto_generated property is only allowed for filesystem modules in build/soong/fsgen directory")
344}
345
346func createNoSha1HashRule() Rule {
347	return NeverAllow().
348		ModuleType("filesystem", "android_filesystem").
349		ModuleType("filesystem", "android_system_image").
350		With("avb_hash_algorithm", "sha1").
351		Because("sha1 is discouraged")
352}
353
354func createKotlinPluginRule() []Rule {
355	kotlinPluginProjectsAllowedList := []string{
356		"external/kotlinc",
357	}
358
359	return []Rule{
360		NeverAllow().
361			NotIn(kotlinPluginProjectsAllowedList...).
362			ModuleType("kotlin_plugin").
363			Because("kotlin_plugin can only be used in allowed projects"),
364	}
365}
366
367// These module types are introduced to convert PRODUCT_COPY_FILES to Soong,
368// and is only intended to be used by filesystem_creator.
369func createPrebuiltEtcBpDefineRule() Rule {
370	return NeverAllow().
371		ModuleType(
372			"prebuilt_any",
373			"prebuilt_usr_srec",
374			"prebuilt_priv_app",
375			"prebuilt_rfs",
376			"prebuilt_framework",
377			"prebuilt_wlc_upt",
378			"prebuilt_odm",
379			"prebuilt_vendor_dlkm",
380			"prebuilt_bt_firmware",
381			"prebuilt_tvservice",
382			"prebuilt_optee",
383			"prebuilt_tvconfig",
384			"prebuilt_vendor",
385			"prebuilt_sbin",
386			"prebuilt_system",
387			"prebuilt_first_stage_ramdisk",
388			"prebuilt_radio",
389			"prebuilt_gpu",
390			"prebuilt_vendor_overlay",
391			"prebuilt_tee",
392		).
393		DefinedInBpFile().
394		Because("module type not allowed to be defined in bp file")
395}
396
397func createAutogenRroBpDefineRule() Rule {
398	return NeverAllow().
399		ModuleType(
400			"autogen_runtime_resource_overlay",
401		).
402		DefinedInBpFile().
403		Because("Module type will be autogenerated by soong. Use runtime_resource_overlay instead")
404}
405
406func neverallowMutator(ctx BottomUpMutatorContext) {
407	m, ok := ctx.Module().(Module)
408	if !ok {
409		return
410	}
411
412	dir := ctx.ModuleDir() + "/"
413	properties := m.GetProperties()
414
415	osClass := ctx.Module().Target().Os.Class
416
417	for _, r := range neverallowRules(ctx.Config()) {
418		n := r.(*rule)
419		if !n.appliesToPath(dir) {
420			continue
421		}
422
423		modType := proptools.StringDefault(m.base().baseProperties.Soong_config_base_module_type, ctx.ModuleType())
424		if !n.appliesToModuleType(modType) {
425			continue
426		}
427
428		if !n.appliesToProperties(ctx, properties) {
429			continue
430		}
431
432		if !n.appliesToOsClass(osClass) {
433			continue
434		}
435
436		if !n.appliesToDirectDeps(ctx) {
437			continue
438		}
439
440		if !n.appliesToBpDefinedModule(ctx) {
441			continue
442		}
443
444		ctx.ModuleErrorf("violates " + n.String())
445	}
446}
447
448type ValueMatcher interface {
449	Test(string) bool
450	String() string
451}
452
453type equalMatcher struct {
454	expected string
455}
456
457func (m *equalMatcher) Test(value string) bool {
458	return m.expected == value
459}
460
461func (m *equalMatcher) String() string {
462	return "=" + m.expected
463}
464
465type anyMatcher struct {
466}
467
468func (m *anyMatcher) Test(value string) bool {
469	return true
470}
471
472func (m *anyMatcher) String() string {
473	return "=*"
474}
475
476var anyMatcherInstance = &anyMatcher{}
477
478type startsWithMatcher struct {
479	prefix string
480}
481
482func (m *startsWithMatcher) Test(value string) bool {
483	return strings.HasPrefix(value, m.prefix)
484}
485
486func (m *startsWithMatcher) String() string {
487	return ".starts-with(" + m.prefix + ")"
488}
489
490type regexMatcher struct {
491	re *regexp.Regexp
492}
493
494func (m *regexMatcher) Test(value string) bool {
495	return m.re.MatchString(value)
496}
497
498func (m *regexMatcher) String() string {
499	return ".regexp(" + m.re.String() + ")"
500}
501
502type notInListMatcher struct {
503	allowed []string
504}
505
506func (m *notInListMatcher) Test(value string) bool {
507	return !InList(value, m.allowed)
508}
509
510func (m *notInListMatcher) String() string {
511	return ".not-in-list(" + strings.Join(m.allowed, ",") + ")"
512}
513
514type InListMatcher struct {
515	allowed []string
516}
517
518func (m *InListMatcher) Test(value string) bool {
519	return InList(value, m.allowed)
520}
521
522func (m *InListMatcher) String() string {
523	return ".in-list(" + strings.Join(m.allowed, ",") + ")"
524}
525
526type isSetMatcher struct{}
527
528func (m *isSetMatcher) Test(value string) bool {
529	return value != ""
530}
531
532func (m *isSetMatcher) String() string {
533	return ".is-set"
534}
535
536var isSetMatcherInstance = &isSetMatcher{}
537
538type ruleProperty struct {
539	fields  []string // e.x.: Vndk.Enabled
540	matcher ValueMatcher
541}
542
543func (r *ruleProperty) String() string {
544	return fmt.Sprintf("%q matches: %s", strings.Join(r.fields, "."), r.matcher)
545}
546
547type ruleProperties []ruleProperty
548
549func (r ruleProperties) String() string {
550	var s []string
551	for _, r := range r {
552		s = append(s, r.String())
553	}
554	return strings.Join(s, " ")
555}
556
557// A NeverAllow rule.
558type Rule interface {
559	In(path ...string) Rule
560
561	NotIn(path ...string) Rule
562
563	InDirectDeps(deps ...string) Rule
564
565	WithOsClass(osClasses ...OsClass) Rule
566
567	ModuleType(types ...string) Rule
568
569	NotModuleType(types ...string) Rule
570
571	With(properties, value string) Rule
572
573	WithMatcher(properties string, matcher ValueMatcher) Rule
574
575	Without(properties, value string) Rule
576
577	WithoutMatcher(properties string, matcher ValueMatcher) Rule
578
579	DefinedInBpFile() Rule
580
581	Because(reason string) Rule
582}
583
584type rule struct {
585	// User string for why this is a thing.
586	reason string
587
588	paths       []string
589	unlessPaths []string
590
591	directDeps map[string]bool
592
593	osClasses []OsClass
594
595	moduleTypes       []string
596	unlessModuleTypes []string
597
598	props       ruleProperties
599	unlessProps ruleProperties
600
601	onlyBootclasspathJar bool
602
603	definedInBp bool
604}
605
606// Create a new NeverAllow rule.
607func NeverAllow() Rule {
608	return &rule{directDeps: make(map[string]bool)}
609}
610
611// In adds path(s) where this rule applies.
612func (r *rule) In(path ...string) Rule {
613	r.paths = append(r.paths, cleanPaths(path)...)
614	return r
615}
616
617// NotIn adds path(s) to that this rule does not apply to.
618func (r *rule) NotIn(path ...string) Rule {
619	r.unlessPaths = append(r.unlessPaths, cleanPaths(path)...)
620	return r
621}
622
623// InDirectDeps adds dep(s) that are not allowed with this rule.
624func (r *rule) InDirectDeps(deps ...string) Rule {
625	for _, d := range deps {
626		r.directDeps[d] = true
627	}
628	return r
629}
630
631// WithOsClass adds osClass(es) that this rule applies to.
632func (r *rule) WithOsClass(osClasses ...OsClass) Rule {
633	r.osClasses = append(r.osClasses, osClasses...)
634	return r
635}
636
637// ModuleType adds type(s) that this rule applies to.
638func (r *rule) ModuleType(types ...string) Rule {
639	r.moduleTypes = append(r.moduleTypes, types...)
640	return r
641}
642
643// NotModuleType adds type(s) that this rule does not apply to..
644func (r *rule) NotModuleType(types ...string) Rule {
645	r.unlessModuleTypes = append(r.unlessModuleTypes, types...)
646	return r
647}
648
649// With specifies property/value combinations that are restricted for this rule.
650func (r *rule) With(properties, value string) Rule {
651	return r.WithMatcher(properties, selectMatcher(value))
652}
653
654// WithMatcher specifies property/matcher combinations that are restricted for this rule.
655func (r *rule) WithMatcher(properties string, matcher ValueMatcher) Rule {
656	r.props = append(r.props, ruleProperty{
657		fields:  fieldNamesForProperties(properties),
658		matcher: matcher,
659	})
660	return r
661}
662
663// Without specifies property/value combinations that this rule does not apply to.
664func (r *rule) Without(properties, value string) Rule {
665	return r.WithoutMatcher(properties, selectMatcher(value))
666}
667
668// Without specifies property/matcher combinations that this rule does not apply to.
669func (r *rule) WithoutMatcher(properties string, matcher ValueMatcher) Rule {
670	r.unlessProps = append(r.unlessProps, ruleProperty{
671		fields:  fieldNamesForProperties(properties),
672		matcher: matcher,
673	})
674	return r
675}
676
677// DefinedInBpFile specifies that this rule applies to modules that are defined
678// in bp files, and does not apply to modules that are auto generated by other modules.
679func (r *rule) DefinedInBpFile() Rule {
680	r.definedInBp = true
681	return r
682}
683
684func selectMatcher(expected string) ValueMatcher {
685	if expected == "*" {
686		return anyMatcherInstance
687	}
688	return &equalMatcher{expected: expected}
689}
690
691// Because specifies a reason for this rule.
692func (r *rule) Because(reason string) Rule {
693	r.reason = reason
694	return r
695}
696
697func (r *rule) String() string {
698	s := []string{"neverallow requirements. Not allowed:"}
699	if len(r.paths) > 0 {
700		s = append(s, fmt.Sprintf("in dirs: %q", r.paths))
701	}
702	if len(r.moduleTypes) > 0 {
703		s = append(s, fmt.Sprintf("module types: %q", r.moduleTypes))
704	}
705	if len(r.props) > 0 {
706		s = append(s, fmt.Sprintf("properties matching: %s", r.props))
707	}
708	if len(r.directDeps) > 0 {
709		s = append(s, fmt.Sprintf("dep(s): %q", SortedKeys(r.directDeps)))
710	}
711	if len(r.osClasses) > 0 {
712		s = append(s, fmt.Sprintf("os class(es): %q", r.osClasses))
713	}
714	if len(r.unlessPaths) > 0 {
715		s = append(s, fmt.Sprintf("EXCEPT in dirs: %q", r.unlessPaths))
716	}
717	if len(r.unlessModuleTypes) > 0 {
718		s = append(s, fmt.Sprintf("EXCEPT module types: %q", r.unlessModuleTypes))
719	}
720	if len(r.unlessProps) > 0 {
721		s = append(s, fmt.Sprintf("EXCEPT properties matching: %q", r.unlessProps))
722	}
723	if len(r.reason) != 0 {
724		s = append(s, " which is restricted because "+r.reason)
725	}
726	if len(s) == 1 {
727		s[0] = "neverallow requirements (empty)"
728	}
729	return strings.Join(s, "\n\t")
730}
731
732func (r *rule) appliesToPath(dir string) bool {
733	includePath := len(r.paths) == 0 || HasAnyPrefix(dir, r.paths)
734	excludePath := HasAnyPrefix(dir, r.unlessPaths)
735	return includePath && !excludePath
736}
737
738func (r *rule) appliesToDirectDeps(ctx BottomUpMutatorContext) bool {
739	if len(r.directDeps) == 0 {
740		return true
741	}
742
743	matches := false
744	ctx.VisitDirectDeps(func(m Module) {
745		if !matches {
746			name := ctx.OtherModuleName(m)
747			matches = r.directDeps[name]
748		}
749	})
750
751	return matches
752}
753
754func (r *rule) appliesToOsClass(osClass OsClass) bool {
755	if len(r.osClasses) == 0 {
756		return true
757	}
758
759	for _, c := range r.osClasses {
760		if c == osClass {
761			return true
762		}
763	}
764
765	return false
766}
767
768func (r *rule) appliesToModuleType(moduleType string) bool {
769	// Remove prefix for auto-generated modules
770	moduleType = strings.TrimSuffix(moduleType, "__loadHookModule")
771	moduleType = strings.TrimSuffix(moduleType, "__bottomUpMutatorModule")
772	return (len(r.moduleTypes) == 0 || InList(moduleType, r.moduleTypes)) && !InList(moduleType, r.unlessModuleTypes)
773}
774
775func (r *rule) appliesToProperties(ctx BottomUpMutatorContext, properties []interface{}) bool {
776	includeProps := hasAllProperties(ctx, properties, r.props)
777	excludeProps := hasAnyProperty(ctx, properties, r.unlessProps)
778	return includeProps && !excludeProps
779}
780
781func (r *rule) appliesToBpDefinedModule(ctx BottomUpMutatorContext) bool {
782	if !r.definedInBp {
783		return true
784	}
785	return !ctx.OtherModuleIsAutoGenerated(ctx.Module()) == r.definedInBp
786}
787
788func StartsWith(prefix string) ValueMatcher {
789	return &startsWithMatcher{prefix}
790}
791
792func Regexp(re string) ValueMatcher {
793	r, err := regexp.Compile(re)
794	if err != nil {
795		panic(err)
796	}
797	return &regexMatcher{r}
798}
799
800func NotInList(allowed []string) ValueMatcher {
801	return &notInListMatcher{allowed}
802}
803
804func InAllowedList(allowed []string) ValueMatcher {
805	return &InListMatcher{allowed}
806}
807
808// assorted utils
809
810func cleanPaths(paths []string) []string {
811	res := make([]string, len(paths))
812	for i, v := range paths {
813		res[i] = filepath.Clean(v) + "/"
814	}
815	return res
816}
817
818func fieldNamesForProperties(propertyNames string) []string {
819	names := strings.Split(propertyNames, ".")
820	for i, v := range names {
821		names[i] = proptools.FieldNameForProperty(v)
822	}
823	return names
824}
825
826func hasAnyProperty(ctx BottomUpMutatorContext, properties []interface{}, props []ruleProperty) bool {
827	for _, v := range props {
828		if hasProperty(ctx, properties, v) {
829			return true
830		}
831	}
832	return false
833}
834
835func hasAllProperties(ctx BottomUpMutatorContext, properties []interface{}, props []ruleProperty) bool {
836	for _, v := range props {
837		if !hasProperty(ctx, properties, v) {
838			return false
839		}
840	}
841	return true
842}
843
844func hasProperty(ctx BottomUpMutatorContext, properties []interface{}, prop ruleProperty) bool {
845	for _, propertyStruct := range properties {
846		propertiesValue := reflect.ValueOf(propertyStruct).Elem()
847		for _, v := range prop.fields {
848			if !propertiesValue.IsValid() {
849				break
850			}
851			propertiesValue = propertiesValue.FieldByName(v)
852		}
853		if !propertiesValue.IsValid() {
854			continue
855		}
856
857		check := func(value string) bool {
858			return prop.matcher.Test(value)
859		}
860
861		if matchValue(ctx, propertiesValue, check) {
862			return true
863		}
864	}
865	return false
866}
867
868func matchValue(ctx BottomUpMutatorContext, value reflect.Value, check func(string) bool) bool {
869	if !value.IsValid() {
870		return false
871	}
872
873	if value.Kind() == reflect.Ptr {
874		if value.IsNil() {
875			return check("")
876		}
877		value = value.Elem()
878	}
879
880	switch v := value.Interface().(type) {
881	case string:
882		return check(v)
883	case bool:
884		return check(strconv.FormatBool(v))
885	case int:
886		return check(strconv.FormatInt((int64)(v), 10))
887	case []string:
888		for _, v := range v {
889			if check(v) {
890				return true
891			}
892		}
893		return false
894	case proptools.Configurable[string]:
895		return check(v.GetOrDefault(ctx, ""))
896	case proptools.Configurable[bool]:
897		return check(strconv.FormatBool(v.GetOrDefault(ctx, false)))
898	case proptools.Configurable[[]string]:
899		for _, v := range v.GetOrDefault(ctx, nil) {
900			if check(v) {
901				return true
902			}
903		}
904		return false
905	}
906
907	panic("Can't handle type: " + value.Kind().String())
908}
909
910var neverallowRulesKey = NewOnceKey("neverallowRules")
911
912func neverallowRules(config Config) []Rule {
913	return config.Once(neverallowRulesKey, func() interface{} {
914		// No test rules were set by setTestNeverallowRules, use the global rules
915		return neverallows
916	}).([]Rule)
917}
918
919// Overrides the default neverallow rules for the supplied config.
920//
921// For testing only.
922func setTestNeverallowRules(config Config, testRules []Rule) {
923	config.Once(neverallowRulesKey, func() interface{} { return testRules })
924}
925
926// Prepares for a test by setting neverallow rules and enabling the mutator.
927//
928// If the supplied rules are nil then the default rules are used.
929func PrepareForTestWithNeverallowRules(testRules []Rule) FixturePreparer {
930	return GroupFixturePreparers(
931		FixtureModifyConfig(func(config Config) {
932			if testRules != nil {
933				setTestNeverallowRules(config, testRules)
934			}
935		}),
936		FixtureRegisterWithContext(func(ctx RegistrationContext) {
937			ctx.PostDepsMutators(registerNeverallowMutator)
938		}),
939	)
940}
941