Check if ./bin entries are not vendor_file

This can detect a common mistake of not labeling binaries in APEX.

Note - we can't simply check if the lable has exec_type attribute
because there're many exceptions.

Bug: 324005965
Test: atest apex_sepolicy_tests_test
Change-Id: Ib643e8b73fac1a3b8851804e58e69b19d32b997d
diff --git a/tests/apex_sepolicy_tests.py b/tests/apex_sepolicy_tests.py
index ab01745..26082cb 100644
--- a/tests/apex_sepolicy_tests.py
+++ b/tests/apex_sepolicy_tests.py
@@ -56,7 +56,16 @@
     pattern: str
 
 
-Matcher = Is | Glob | Regex
+@dataclass
+class BinaryFile:
+    pass
+
+
+Matcher = Is | Glob | Regex | BinaryFile
+
+
+# predicate functions for Func matcher
+
 
 @dataclass
 class AllowPerm:
@@ -72,7 +81,13 @@
     pass
 
 
-Rule = AllowPerm | ResolveType
+@dataclass
+class NotAnyOf:
+    """Rule checking if entity is not labelled as any of the given labels"""
+    labels: set[str]
+
+
+Rule = AllowPerm | ResolveType | NotAnyOf
 
 
 # Helper for 'read'
@@ -89,6 +104,8 @@
             return pathlib.PurePath(path).match(pattern)
         case Regex(pattern):
             return re.match(pattern, path)
+        case BinaryFile:
+            return path.startswith('./bin/') and not path.endswith('/')
 
 
 def check_rule(pol, path: str, tcontext: str, rule: Rule) -> List[str]:
@@ -109,6 +126,9 @@
         case ResolveType():
             if tcontext not in pol.GetAllTypes(False):
                 errors.append(f"Error: {path}: tcontext({tcontext}) is unknown")
+        case NotAnyOf(labels):
+            if tcontext in labels:
+                errors.append(f"Error: {path}: can't be labelled as '{tcontext}'")
     return errors
 
 
@@ -118,6 +138,8 @@
 
 
 generic_rules = [
+    # binaries should be executable
+    (BinaryFile, NotAnyOf({'vendor_file'})),
     # permissions
     (Is('./etc/permissions/'), AllowRead('dir', {'system_server'})),
     (Glob('./etc/permissions/*.xml'), AllowRead('file', {'system_server'})),
diff --git a/tests/apex_sepolicy_tests_test.py b/tests/apex_sepolicy_tests_test.py
index 3fee43d..727a023 100644
--- a/tests/apex_sepolicy_tests_test.py
+++ b/tests/apex_sepolicy_tests_test.py
@@ -102,5 +102,11 @@
         self.assert_error('./bin/hw/foo u:object_r:foo_exec:s0',
                         r'Error: \./bin/hw/foo: tcontext\(foo_exec\) is unknown')
 
+    def test_binaries(self):
+        self.assert_ok('./bin/init u:object_r:init_exec:s0')
+        self.assert_ok('./bin/hw/svc u:object_r:init_exec:s0')
+        self.assert_error('./bin/hw/svc u:object_r:vendor_file:s0',
+                          r"Error: .*svc: can\'t be labelled as \'vendor_file\'")
+
 if __name__ == '__main__':
     unittest.main(verbosity=2)