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