Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package apparmor for openSUSE:Factory 
checked in at 2024-05-28 22:03:24
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/apparmor (Old)
 and      /work/SRC/openSUSE:Factory/.apparmor.new.24587 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "apparmor"

Tue May 28 22:03:24 2024 rev:208 rq:1177404 version:4.0.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/apparmor/apparmor.changes        2024-05-28 
17:27:21.704810862 +0200
+++ /work/SRC/openSUSE:Factory/.apparmor.new.24587/apparmor.changes     
2024-05-28 22:03:25.717714068 +0200
@@ -1,0 +2,9 @@
+Tue May 28 19:34:43 UTC 2024 - Christian Boltz <suse-b...@cboltz.de>
+
+- add utils-relax-mount-rules.diff and utils-relax-mount-rules-2.diff:
+  Relax handling of mount rules in utils to avoid errors when
+  parsing valid profiles
+- add teardown-unconfined.diff to fix aa-teardown for 'unconfined'
+  profiles (boo#1225457)
+
+-------------------------------------------------------------------

New:
----
  teardown-unconfined.diff
  utils-relax-mount-rules-2.diff
  utils-relax-mount-rules.diff

BETA DEBUG BEGIN:
  New:  parsing valid profiles
- add teardown-unconfined.diff to fix aa-teardown for 'unconfined'
  profiles (boo#1225457)
  New:
- add utils-relax-mount-rules.diff and utils-relax-mount-rules-2.diff:
  Relax handling of mount rules in utils to avoid errors when
  New:
- add utils-relax-mount-rules.diff and utils-relax-mount-rules-2.diff:
  Relax handling of mount rules in utils to avoid errors when
BETA DEBUG END:

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ apparmor.spec ++++++
--- /var/tmp/diff_new_pack.ZAAL2q/_old  2024-05-28 22:03:26.973759983 +0200
+++ /var/tmp/diff_new_pack.ZAAL2q/_new  2024-05-28 22:03:26.977760130 +0200
@@ -88,9 +88,16 @@
 # make test-aa-notify a bit more relaxed to allow different argparse wording 
on Leap 15.5 (merged upstream 2024-05-06 (4.0 and master) 
https://gitlab.com/apparmor/apparmor/-/merge_requests/1226)
 Patch11:        test-aa-notify.diff
 
-# Fix aa-remove-unknown for 'unconfined' profiles (submitted upstream 
2024-05-25 https://gitlab.com/apparmor/apparmor/-/merge_requests/1240)
+# Fix aa-remove-unknown for 'unconfined' profiles (merged upstream 2024-05-28 
in 4.0 and master https://gitlab.com/apparmor/apparmor/-/merge_requests/1240)
 Patch12:        aa-remove-unknown-fix-unconfined.diff
 
+# Fix aa-teardown for 'unconfined' profiles (submitted upstream 2024-05-28 
https://gitlab.com/apparmor/apparmor/-/merge_requests/1242)
+Patch13:        teardown-unconfined.diff
+
+# Relax handling of mount rules in utils to avoid errors when parsing valid 
profiles (both patches taken from upstream 4.0 branch 2024-05-28)
+Patch14:        utils-relax-mount-rules.diff
+Patch15:        utils-relax-mount-rules-2.diff
+
 PreReq:         sed
 BuildRoot:      %{_tmppath}/%{name}-%{version}-build
 BuildRequires:  autoconf
@@ -361,6 +368,9 @@
 %patch -P 10 -p1
 %patch -P 11 -p1
 %patch -P 12 -p1
+%patch -P 13 -p1
+%patch -P 14 -p1
+%patch -P 15 -p1
 
 %build
 export SUSE_ASNEEDED=0



++++++ teardown-unconfined.diff ++++++
commit f497afbe1364b45540a6582870e5a76f1ada7a2b
Author: Christian Boltz <appar...@cboltz.de>
Date:   Tue May 28 21:13:47 2024 +0200

    Fix aa-teardown for `unconfined` profiles
    
    Fixes: https://bugzilla.opensuse.org/show_bug.cgi?id=1225457

diff --git a/parser/rc.apparmor.functions b/parser/rc.apparmor.functions
index f66fea422..099ab67d3 100644
--- a/parser/rc.apparmor.functions
+++ b/parser/rc.apparmor.functions
@@ -253,7 +253,7 @@ remove_profiles() {
        retval=0
        # We filter child profiles as removing the parent will remove
        # the children
-       sed -e "s/ (\(enforce\|complain\))$//" "$SFS_MOUNTPOINT/profiles" | \
+       sed -e "s/ (\(enforce\|complain\|unconfined\))$//" 
"$SFS_MOUNTPOINT/profiles" | \
        LC_COLLATE=C sort | grep -v // | {
                while read -r profile ; do
                        printf "%s" "$profile" > "$SFS_MOUNTPOINT/.remove"

++++++ utils-relax-mount-rules-2.diff ++++++
commit 1f4bba0448563b7d1fe4d86c230556ebf8d3805b
Author: Maxime Bélair <maxime.bel...@canonical.com>
Date:   Mon May 20 11:09:04 2024 +0200

    Cherry-pick: MountRule: Aligning behavior with apparmor_parser
    
    Mount Rules with options in { remount, [make-] { [r]unbindable, [r]shared, 
[r]private, and [r]slave }} do not support specifying a source. This commit 
aligns utils implementation to apparmor_parser's, which prohibits having a both 
source and a destination simultaneously, instad of just prohibiting source.
    
    Therefore, both `mount options=(unbindable) /a,` and `mount 
options=(unbindable) -> /a,` are now supported (and equivalent for 
apparmor_parser). However, `mount options=(unbindable) /a -> /b,` is invalid.
    
    For the same reason, specifying a fstype in these cases is also prohibited.
    
    Similarly, we prohibit to specify a fstype for bind mount rules.
    
    Fixes: https://bugs.launchpad.net/ubuntu/+source/apparmor/+bug/2065685
    
    (cherry picked from commit 60acc4a4053ddb3718b9a2f5ceb1ef02fea3a226)
    
    Signed-off-by: Maxime Bélair <maxime.bel...@canonical.com>

diff --git a/utils/apparmor/rule/mount.py b/utils/apparmor/rule/mount.py
index abfa2b75e..b2d73a27b 100644
--- a/utils/apparmor/rule/mount.py
+++ b/utils/apparmor/rule/mount.py
@@ -25,15 +25,18 @@ _ = init_translation()
 
 # TODO : Apparmor remount logs are displayed as mount (with remount flag). 
Profiles generated with aa-genprof are therefore mount rules. It could be 
interesting to make them remount rules.
 
-flags_keywords = [
-    # keep in sync with parser/mount.cc mnt_opts_table!
-    'ro', 'r', 'read-only', 'rw', 'w', 'suid', 'nosuid', 'dev', 'nodev', 
'exec', 'noexec', 'sync', 'async', 'remount',
-    'mand', 'nomand', 'dirsync', 'symfollow', 'nosymfollow', 'atime', 
'noatime', 'diratime', 'nodiratime', 'bind', 'B',
-    'move', 'M', 'rbind', 'R', 'verbose', 'silent', 'loud', 'acl', 'noacl', 
'unbindable', 'make-unbindable', 'runbindable',
-    'make-runbindable', 'private', 'make-private', 'rprivate', 
'make-rprivate', 'slave', 'make-slave', 'rslave', 'make-rslave',
-    'shared', 'make-shared', 'rshared', 'make-rshared', 'relatime', 
'norelatime', 'iversion', 'noiversion', 'strictatime',
-    'nostrictatime', 'lazytime', 'nolazytime', 'user', 'nouser',
-    '([A-Za-z0-9])',
+flags_bind_mount = {'B', 'bind', 'R', 'rbind'}
+flags_change_propagation = {
+    'remount', 'unbindable', 'shared', 'private', 'slave', 'runbindable', 
'rshared', 'rprivate', 'rslave',
+    'make-unbindable', 'make-shared', 'make-private', 'make-slave', 
'make-runbindable', 'make-rshared', 'make-rprivate',
+    'make-rslave'
+}
+# keep in sync with parser/mount.cc mnt_opts_table!
+flags_keywords = list(flags_bind_mount) + list(flags_change_propagation) + [
+    'ro', 'r', 'read-only', 'rw', 'w', 'suid', 'nosuid', 'dev', 'nodev', 
'exec', 'noexec', 'sync', 'async', 'mand',
+    'nomand', 'dirsync', 'symfollow', 'nosymfollow', 'atime', 'noatime', 
'diratime', 'nodiratime', 'move', 'M',
+    'verbose', 'silent', 'loud', 'acl', 'noacl', 'relatime', 'norelatime', 
'iversion', 'noiversion', 'strictatime',
+    'nostrictatime', 'lazytime', 'nolazytime', 'user', 'nouser', 
'([A-Za-z0-9])',
 ]
 join_valid_flags = '|'.join(flags_keywords)
 
@@ -112,6 +115,7 @@ class MountRule(BaseRule):
         self.is_options_equal = options[0] if not self.all_options else None
 
         self.source, self.all_source = self._aare_or_all(source, 'source', 
is_path=False, log_event=log_event)
+        self.dest, self.all_dest = self._aare_or_all(dest, 'dest', 
is_path=False, log_event=log_event)
 
         if not self.all_fstype and self.is_fstype_equal not in ('=', 'in'):
             raise AppArmorBug(f'Invalid is_fstype_equal : 
{self.is_fstype_equal}')
@@ -120,11 +124,14 @@ class MountRule(BaseRule):
         if self.operation != 'mount' and not self.all_source:
             raise AppArmorException(f'Operation {self.operation} cannot have a 
source')
 
-        flags_forbidden_with_source = {'remount', 'unbindable', 'shared', 
'private', 'slave', 'runbindable', 'rshared', 'rprivate', 'rslave'}
-        if self.operation == 'mount' and not self.all_source and not 
self.all_options and flags_forbidden_with_source & self.options != set():
-            raise AppArmorException(f'Operation {flags_forbidden_with_source & 
self.options} cannot have a source. Source = {self.source}')
+        if self.operation == 'mount' and not self.all_options and 
flags_change_propagation & self.options != set():
+            if not (self.all_source or self.all_dest):
+                raise AppArmorException(f'Operation {flags_change_propagation 
& self.options} cannot specify a source. Source = {self.source}')
+            elif not self.all_fstype:
+                raise AppArmorException(f'Operation {flags_change_propagation 
& self.options} cannot specify a fstype. Fstype = {self.fstype}')
 
-        self.dest, self.all_dest = self._aare_or_all(dest, 'dest', 
is_path=False, log_event=log_event)
+        if self.operation == 'mount' and not self.all_options and 
flags_bind_mount & self.options != set() and not self.all_fstype:
+            raise AppArmorException(f'Bind mount rules cannot specify a 
fstype. Fstype = {self.fstype}')
 
         self.can_glob = not self.all_source and not self.all_dest and not 
self.all_options
 
diff --git a/utils/test/test-mount.py b/utils/test/test-mount.py
index 7f88ff7db..cb1a89630 100644
--- a/utils/test/test-mount.py
+++ b/utils/test/test-mount.py
@@ -43,12 +43,12 @@ class MountTestParse(AATest):
         ('mount @{mntpnt},',                                                
MountRule('mount',   MountRule.ALL,            MountRule.ALL,           
'@{mntpnt}',    MountRule.ALL,  False, False, False, ''     )),
         ('mount /a,',                                                       
MountRule('mount',   MountRule.ALL,            MountRule.ALL,           '/a',   
        MountRule.ALL,  False, False, False, ''     )),
         ('mount fstype=(ext3, ext4) /a -> /b,',                             
MountRule('mount',   ('=', ['ext3', 'ext4']),  MountRule.ALL,           '/a',   
        '/b',           False, False, False, ''     )),
-        ('mount fstype=(ext3, ext4) options=(ro, rbind) /a -> /b,',         
MountRule('mount',   ('=', ['ext3', 'ext4']),  ('=', ('ro', 'rbind')),  '/a',   
        '/b',           False, False, False, ''     )),
-        ('mount fstype=(ext3, ext4) options=(ro, rbind) /a -> /b, #cmt',    
MountRule('mount',   ('=', ['ext3', 'ext4']),  ('=', ('ro', 'rbind')),  '/a',   
        '/b',           False, False, False, ' #cmt')),
-        ('mount fstype=({ext3,ext4}) options in (ro, rbind) /a -> /b,',     
MountRule('mount',   ('=', ['{ext3,ext4}']),   ('in', ('ro', 'rbind')), '/a',   
        '/b',           False, False, False, ''     )),
-        ('mount fstype in (ext3, ext4) options=(ro, rbind) /a -> /b, #cmt', 
MountRule('mount',   ('in', ['ext3', 'ext4']), ('=', ('ro', 'rbind')),  '/a',   
        '/b',           False, False, False, ' #cmt')),
-        ('mount fstype in (ext3, ext4) option in (ro, rbind) /a, #cmt',     
MountRule('mount',   ('in', ['ext3', 'ext4']), ('in', ('ro', 'rbind')), '/a',   
        MountRule.ALL,  False, False, False, ' #cmt')),
-        ('mount fstype=(ext3, ext4) option=(ro, rbind) /a -> /b, #cmt',     
MountRule('mount',   ('=', ['ext3', 'ext4']),  ('=', ('ro', 'rbind')),  '/a',   
        '/b',           False, False, False, ' #cmt')),
+        ('mount fstype=(ext3, ext4) options=(ro, sync) /a -> /b,',          
MountRule('mount',   ('=', ['ext3', 'ext4']),  ('=', ('ro', 'sync')),   '/a',   
        '/b',           False, False, False, ''     )),
+        ('mount fstype=(ext3, ext4) options=(ro, sync) /a -> /b, #cmt',     
MountRule('mount',   ('=', ['ext3', 'ext4']),  ('=', ('ro', 'sync')),   '/a',   
        '/b',           False, False, False, ' #cmt')),
+        ('mount fstype=({ext3,ext4}) options in (ro, sync) /a -> /b,',      
MountRule('mount',   ('=', ['{ext3,ext4}']),   ('in', ('ro', 'sync')),  '/a',   
        '/b',           False, False, False, ''     )),
+        ('mount fstype in (ext3, ext4) options=(ro, sync) /a -> /b, #cmt',  
MountRule('mount',   ('in', ['ext3', 'ext4']), ('=', ('ro', 'sync')),   '/a',   
        '/b',           False, False, False, ' #cmt')),
+        ('mount fstype in (ext3, ext4) option in (ro, sync) /a, #cmt',      
MountRule('mount',   ('in', ['ext3', 'ext4']), ('in', ('ro', 'sync')),  '/a',   
        MountRule.ALL,  False, False, False, ' #cmt')),
+        ('mount fstype=(ext3, ext4) option=(ro, sync) /a -> /b, #cmt',      
MountRule('mount',   ('=', ['ext3', 'ext4']),  ('=', ('ro', 'sync')),   '/a',   
        '/b',           False, False, False, ' #cmt')),
         ('mount options=(rw, rbind) {,/usr}/lib{,32,64,x32}/modules/ -> 
/tmp/snap.rootfs_*{,/usr}/lib/modules/,',
                                                                             
MountRule('mount',   MountRule.ALL,            ('=', ('rw', 'rbind')),  
'{,/usr}/lib{,32,64,x32}/modules/',
                                                                                
                                                                                
    '/tmp/snap.rootfs_*{,/usr}/lib/modules/',
@@ -68,6 +68,17 @@ class MountTestParse(AATest):
         expected.raw_rule = rawrule.strip()
         self.assertTrue(obj.is_equal(expected, True))
 
+    def test_valid_mount_changing_propagation(self):
+        # Rules changing propagation type can either specify a source or a 
dest (these are equivalent for apparmor_parser in this specific case) but not 
both.
+        MountRule('mount', MountRule.ALL, ('=', ('runbindable')), '/foo', 
MountRule.ALL)
+        MountRule('mount', MountRule.ALL, ('=', ('runbindable')), 
MountRule.ALL, '/foo')
+
+    def test_valid_bind_mount(self):
+        # Fstype must remain empty in bind rules
+        MountRule('mount', MountRule.ALL, ('=', ('bind')), '/foo', 
MountRule.ALL)
+        MountRule('mount', MountRule.ALL, ('=', ('bind')), MountRule.ALL, 
'/bar')
+        MountRule('mount', MountRule.ALL, ('=', ('bind')), '/foo', '/bar')
+
 
 class MountTestParseInvalid(AATest):
     tests = (
@@ -143,6 +154,20 @@ class MountTestParseInvalid(AATest):
         with self.assertRaises(AppArmorException):
             MountRule('remount', MountRule.ALL, MountRule.ALL, '/foo', 
MountRule.ALL)
 
+    def test_invalid_mount_changing_propagation(self):
+        # Rules changing propagation type can either specify a source or a 
dest (these are equivalent for apparmor_parser in this specific case) but not 
both.
+        with self.assertRaises(AppArmorException):
+            MountRule('mount', MountRule.ALL, ('=', ('runbindable')), '/foo', 
'/bar')
+
+        # Rules changing propagation type cannot specify a fstype.
+        with self.assertRaises(AppArmorException):
+            MountRule('mount', ('=', ('ext4')), ('=', ('runbindable')), 
MountRule.ALL, '/foo')
+
+    def test_invalid_bind_mount(self):
+        # Bind mount rules cannot specify a fstype.
+        with self.assertRaises(AppArmorException):
+            MountRule('mount', ('=', ('ext4')), ('=', ('bind')), 
MountRule.ALL, '/foo')
+
 
 class MountTestGlob(AATest):
     def test_glob(self):
diff --git a/utils/test/test-parser-simple-tests.py 
b/utils/test/test-parser-simple-tests.py
index 451af7d22..60a738aed 100644
--- a/utils/test/test-parser-simple-tests.py
+++ b/utils/test/test-parser-simple-tests.py
@@ -85,16 +85,6 @@ exception_not_raised = (
     'mount/bad_1.sd',
     'mount/bad_2.sd',
 
-    # not checked/detected: "make-*" mount opt and an invalid src
-    'mount/bad_opt_17.sd',
-    'mount/bad_opt_18.sd',
-    'mount/bad_opt_19.sd',
-    'mount/bad_opt_20.sd',
-    'mount/bad_opt_21.sd',
-    'mount/bad_opt_22.sd',
-    'mount/bad_opt_23.sd',
-    'mount/bad_opt_24.sd',
-
     'profile/flags/flags_bad10.sd',
     'profile/flags/flags_bad11.sd',
     'profile/flags/flags_bad12.sd',
@@ -324,19 +314,6 @@ unknown_line = (
     'bare_include_tests/ok_85.sd',
     'bare_include_tests/ok_86.sd',
 
-    # Mount with flags in {remount, [r]unbindable, [r]shared, [r]private, 
[r]slave} does not support a source
-    'mount/ok_opt_68.sd',
-    'mount/ok_opt_69.sd',
-    'mount/ok_opt_70.sd',
-    'mount/ok_opt_71.sd',
-    'mount/ok_opt_72.sd',
-    'mount/ok_opt_73.sd',
-    'mount/ok_opt_74.sd',
-    'mount/ok_opt_75.sd',
-
-    # options=slave with /** src (first rule in the test causes exception)
-    'mount/ok_opt_84.sd',
-
     # According to spec mount should be in the form fstype=... options=... and 
NOT in the form options=... fstype=...
     'mount/ok_opt_combo_3.sd',
     'mount/ok_opt_combo_2.sd',

++++++ utils-relax-mount-rules.diff ++++++
commit eee50538da9a240bc151f26c6cff309808d33590
Author: Georgia Garcia <georgia.gar...@canonical.com>
Date:   Wed May 8 12:58:42 2024 +0000

    Merge MountRule: Relaxing constraints on fstype and completing AARE support
    
     - Before this commit, fstype had to match a known fs. However, having and 
maintaining the exhaustive list of fstypes proved challenging (see !1195 and 
!1176). Therefore, we add support for any filesystem name.
     - Completing AARE support for fstype (brace expressions like ext{3,4} are 
now supported).
    
    MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1198
    Approved-by: Christian Boltz <appar...@cboltz.de>
    Merged-by: Christian Boltz <appar...@cboltz.de>
    
    
    (cherry picked from commit baa8b67248f3467cde40683600d7a945b05f9a3b)
    
    dad5ee28 MountRule: Relaxing constraints on fstype and completing AARE 
support
    
    Co-authored-by: Christian Boltz <appar...@cboltz.de>

diff --git a/utils/apparmor/rule/mount.py b/utils/apparmor/rule/mount.py
index f62c08e4b..abfa2b75e 100644
--- a/utils/apparmor/rule/mount.py
+++ b/utils/apparmor/rule/mount.py
@@ -23,19 +23,7 @@ from apparmor.translations import init_translation
 
 _ = init_translation()
 
-# TODO :
-#   - match correctly AARE on every field
-#   - Find the actual list of supported filesystems. This one comes from 
/proc/filesystems. We also blindly accept fuse.*
-#   - Support path that begin by { (e.g. {,/usr}/lib/...) This syntax is not a 
valid AARE but is used by usr.lib.snapd.snap-confine.real in Ubuntu and will 
currently raise an error in genprof if these lines are not modified.
-#   - Apparmor remount logs are displayed as mount (with remount flag). 
Profiles generated with aa-genprof are therefore mount rules. It could be 
interesting to make them remount rules.
-
-valid_fs = [
-    'sysfs', 'tmpfs', 'bdevfs', 'procfs', 'cgroup', 'cgroup2', 'cpuset', 
'devtmpfs', 'configfs', 'debugfs', 'tracefs',
-    'securityfs', 'sockfs', 'bpf', 'npipefs', 'ramfs', 'hugetlbfs', 'devpts', 
'ext3', 'ext2', 'ext4', 'squashfs',
-    'vfat', 'ecryptfs', 'fuseblk', 'fuse', 'fusectl', 'efivarfs', 'mqueue', 
'store', 'autofs', 'binfmt_misc', 'overlay',
-    'none', 'bdev', 'proc', 'pipefs', 'pstore', 'btrfs', 'xfs', '9p', 
'resctrl', 'zfs', 'iso9660', 'udf', 'ntfs3',
-    'nfs', 'cifs', 'overlayfs', 'aufs', 'rpc_pipefs', 'msdos', 'nfs4',
-]
+# TODO : Apparmor remount logs are displayed as mount (with remount flag). 
Profiles generated with aa-genprof are therefore mount rules. It could be 
interesting to make them remount rules.
 
 flags_keywords = [
     # keep in sync with parser/mount.cc mnt_opts_table!
@@ -48,7 +36,6 @@ flags_keywords = [
     '([A-Za-z0-9])',
 ]
 join_valid_flags = '|'.join(flags_keywords)
-join_valid_fs = '|'.join(valid_fs)
 
 sep = r'\s*[\s,]\s*'
 
@@ -106,27 +93,18 @@ class MountRule(BaseRule):
 
         self.operation = operation
 
-        self.fstype, self.all_fstype, unknown_items = 
check_and_split_list(fstype[1] if fstype != self.ALL else fstype, valid_fs, 
self.ALL, type(self).__name__, 'fstype')
-
-        if unknown_items:
-            for it in unknown_items:
-
-                # Several filesystems use fuse internally and are referred as 
fuse.<software_name> (e.g. fuse.jmtpfs, fuse.s3fs, fuse.obexfs).
-                # Since this list seems to evolve too fast for a fixed list to 
work in practice, we just accept fuse.*
-                # See https://github.com/libfuse/libfuse/wiki/Filesystems and, 
https://doc.ubuntu-fr.org/fuse
-                if it.startswith('fuse.') and len(it) > 5:
-                    continue
-
-                it = AARE(it, is_path=False)
-                found = False
-                for fs in valid_fs:
-                    if self._is_covered_aare(it, self.all_fstype, AARE(fs, 
False), self.all_fstype, 'fstype'):
-                        found = True
-                        break
-                if not found:
-                    raise AppArmorException(_('Passed unknown fstype keyword 
to %s: %s') % (type(self).__name__, ' '.join(unknown_items)))
-
-        self.is_fstype_equal = fstype[0] if not self.all_fstype else None
+        if fstype == self.ALL or fstype[1] == self.ALL:
+            self.all_fstype = True
+            self.fstype = None
+            self.is_fstype_equal = None
+        else:
+            self.all_fstype = False
+            for it in fstype[1]:
+                l, unused = parse_aare(it, 0, 'fstype')
+                if l != len(it):
+                    raise AppArmorException(f'Invalid aare : {it}')
+            self.fstype = fstype[1]
+            self.is_fstype_equal = fstype[0]
 
         self.options, self.all_options, unknown_items = 
check_and_split_list(options[1] if options != self.ALL else options, 
flags_keywords, self.ALL, type(self).__name__, 'options')
         if unknown_items:
@@ -173,7 +151,7 @@ class MountRule(BaseRule):
 
             if r['fstype'] is not None:
                 is_fstype_equal = r['fstype_equals_or_in']
-                fstype = strip_parenthesis(r['fstype']).replace(',', ' 
').split()
+                fstype = parse_aare_list(strip_parenthesis(r['fstype']), 
'fstype')
             else:
                 is_fstype_equal = None
                 fstype = cls.ALL
@@ -316,6 +294,38 @@ class MountRuleset(BaseRuleset):
     '''Class to handle and store a collection of Mount rules'''
 
 
+
+def parse_aare(s, offset, param):
+    parsed = ''
+    brace_count = 0
+    for i, c in enumerate(s[offset:], start=offset):
+        if c in [' ', ',', '\t'] and brace_count == 0:
+            break
+        parsed += c
+        if c == '{':
+            brace_count += 1
+        elif c == '}':
+            brace_count -= 1
+            if brace_count < 0:
+                raise AppArmorException(f"Unmatched closing brace in {param}: 
{s[offset:]}")
+        offset = i
+
+    if brace_count != 0:
+        raise AppArmorException(f"Unmatched opening brace in {param}: 
{s[offset:]}")
+
+    return offset + 1, parsed
+
+
+def parse_aare_list(s, param):
+    res = []
+    offset = 0
+    while offset <= len(s):
+        offset, part = parse_aare(s, offset, param)
+        if part.translate(' ,\t') != '':
+            res.append(part)
+    return res
+
+
 def wrap_in_with_spaces(value):
     ''' wrap 'in' keyword in spaces, and leave everything else unchanged '''
 
diff --git a/utils/test/test-mount.py b/utils/test/test-mount.py
index e37c287c7..7f88ff7db 100644
--- a/utils/test/test-mount.py
+++ b/utils/test/test-mount.py
@@ -20,7 +20,7 @@ from common_test import AATest, setup_all_loops
 from apparmor.common import AppArmorException, AppArmorBug
 from apparmor.translations import init_translation
 
-from apparmor.rule.mount import MountRule, valid_fs
+from apparmor.rule.mount import MountRule
 
 _ = init_translation()
 
@@ -31,34 +31,34 @@ class MountTestParse(AATest):
         #                   Rule                                               
      Operation   Filesystem                Options                  Source     
     Destination     Audit  Deny   Allow  Comment
         ('mount -> **,',                                                    
MountRule('mount',   MountRule.ALL,            MountRule.ALL,           
MountRule.ALL,  '**',           False, False, False, ''     )),
         ('mount options=(rw, shared) -> **,',                               
MountRule('mount',   MountRule.ALL,            ('=', ('rw', 'shared')), 
MountRule.ALL,  '**',           False, False, False, ''     )),
-        ('mount fstype=bpf options=rw bpf -> /sys/fs/bpf/,',                
MountRule('mount',   ('=', ('bpf')),           ('=', ('rw')),           'bpf',  
        '/sys/fs/bpf/', False, False, False, ''     )),
-        ('mount fstype=fuse.obex* options=rw bpf -> /sys/fs/bpf/,',         
MountRule('mount',   ('=', ('fuse.obex*')),    ('=', ('rw')),           'bpf',  
        '/sys/fs/bpf/', False, False, False, ''     )),
-        ('mount fstype=fuse.* options=rw bpf -> /sys/fs/bpf/,',             
MountRule('mount',   ('=', ('fuse.*')),        ('=', ('rw')),           'bpf',  
        '/sys/fs/bpf/', False, False, False, ''     )),
-        ('mount fstype=bpf options=(rw) random_label -> /sys/fs/bpf/,',     
MountRule('mount',   ('=', ("bpf")),           ('=', ('rw')),           
'random_label', '/sys/fs/bpf/', False, False, False, ''     )),
+        ('mount fstype=bpf options=rw bpf -> /sys/fs/bpf/,',                
MountRule('mount',   ('=', ['bpf']),           ('=', ('rw')),           'bpf',  
        '/sys/fs/bpf/', False, False, False, ''     )),
+        ('mount fstype=fuse.obex* options=rw bpf -> /sys/fs/bpf/,',         
MountRule('mount',   ('=', ['fuse.obex*']),    ('=', ('rw')),           'bpf',  
        '/sys/fs/bpf/', False, False, False, ''     )),
+        ('mount fstype=fuse.* options=rw bpf -> /sys/fs/bpf/,',             
MountRule('mount',   ('=', ['fuse.*']),        ('=', ('rw')),           'bpf',  
        '/sys/fs/bpf/', False, False, False, ''     )),
+        ('mount fstype=bpf options=(rw) random_label -> /sys/fs/bpf/,',     
MountRule('mount',   ('=', ['bpf']),           ('=', ('rw')),           
'random_label', '/sys/fs/bpf/', False, False, False, ''     )),
         ('mount,',                                                          
MountRule('mount',   MountRule.ALL,            MountRule.ALL,           
MountRule.ALL,  MountRule.ALL,  False, False, False, ''     )),
-        ('mount fstype=(ext3, ext4),',                                      
MountRule('mount',   ('=', ('ext3', 'ext4')),  MountRule.ALL,           
MountRule.ALL,  MountRule.ALL,  False, False, False, ''     )),
+        ('mount fstype=(ext3, ext4),',                                      
MountRule('mount',   ('=', ['ext3', 'ext4']),  MountRule.ALL,           
MountRule.ALL,  MountRule.ALL,  False, False, False, ''     )),
         ('mount bpf,',                                                      
MountRule('mount',   MountRule.ALL,            MountRule.ALL,           'bpf',  
        MountRule.ALL,  False, False, False, ''     )),
         ('mount none,',                                                     
MountRule('mount',   MountRule.ALL,            MountRule.ALL,           'none', 
        MountRule.ALL,  False, False, False, ''     )),
-        ('mount fstype=(ext3, ext4) options=(ro),',                         
MountRule('mount',   ('=', ('ext3', 'ext4')),  ('=', ('ro')),           
MountRule.ALL,  MountRule.ALL,  False, False, False, ''     )),
+        ('mount fstype=(ext3, ext4) options=(ro),',                         
MountRule('mount',   ('=', ['ext3', 'ext4']),  ('=', ('ro')),           
MountRule.ALL,  MountRule.ALL,  False, False, False, ''     )),
         ('mount @{mntpnt},',                                                
MountRule('mount',   MountRule.ALL,            MountRule.ALL,           
'@{mntpnt}',    MountRule.ALL,  False, False, False, ''     )),
         ('mount /a,',                                                       
MountRule('mount',   MountRule.ALL,            MountRule.ALL,           '/a',   
        MountRule.ALL,  False, False, False, ''     )),
-        ('mount fstype=(ext3, ext4) /a -> /b,',                             
MountRule('mount',   ('=', ('ext3', 'ext4')),  MountRule.ALL,           '/a',   
        '/b',           False, False, False, ''     )),
-        ('mount fstype=(ext3, ext4) options=(ro, rbind) /a -> /b,',         
MountRule('mount',   ('=', ('ext3', 'ext4')),  ('=', ('ro', 'rbind')),  '/a',   
        '/b',           False, False, False, ''     )),
-        ('mount fstype=(ext3, ext4) options=(ro, rbind) /a -> /b, #cmt',    
MountRule('mount',   ('=', ('ext3', 'ext4')),  ('=', ('ro', 'rbind')),  '/a',   
        '/b',           False, False, False, ' #cmt')),
-        ('mount fstype=(ext3, ext4) options in (ro, rbind) /a -> /b,',      
MountRule('mount',   ('=', ('ext3', 'ext4')),  ('in', ('ro', 'rbind')), '/a',   
        '/b',           False, False, False, ''     )),
-        ('mount fstype in (ext3, ext4) options=(ro, rbind) /a -> /b, #cmt', 
MountRule('mount',   ('in', ('ext3', 'ext4')), ('=', ('ro', 'rbind')),  '/a',   
        '/b',           False, False, False, ' #cmt')),
-        ('mount fstype in (ext3, ext4) option in (ro, rbind) /a, #cmt',     
MountRule('mount',   ('in', ('ext3', 'ext4')), ('in', ('ro', 'rbind')), '/a',   
        MountRule.ALL,  False, False, False, ' #cmt')),
-        ('mount fstype=(ext3, ext4) option=(ro, rbind) /a -> /b, #cmt',     
MountRule('mount',   ('=', ('ext3', 'ext4')),  ('=', ('ro', 'rbind')),  '/a',   
        '/b',           False, False, False, ' #cmt')),
+        ('mount fstype=(ext3, ext4) /a -> /b,',                             
MountRule('mount',   ('=', ['ext3', 'ext4']),  MountRule.ALL,           '/a',   
        '/b',           False, False, False, ''     )),
+        ('mount fstype=(ext3, ext4) options=(ro, rbind) /a -> /b,',         
MountRule('mount',   ('=', ['ext3', 'ext4']),  ('=', ('ro', 'rbind')),  '/a',   
        '/b',           False, False, False, ''     )),
+        ('mount fstype=(ext3, ext4) options=(ro, rbind) /a -> /b, #cmt',    
MountRule('mount',   ('=', ['ext3', 'ext4']),  ('=', ('ro', 'rbind')),  '/a',   
        '/b',           False, False, False, ' #cmt')),
+        ('mount fstype=({ext3,ext4}) options in (ro, rbind) /a -> /b,',     
MountRule('mount',   ('=', ['{ext3,ext4}']),   ('in', ('ro', 'rbind')), '/a',   
        '/b',           False, False, False, ''     )),
+        ('mount fstype in (ext3, ext4) options=(ro, rbind) /a -> /b, #cmt', 
MountRule('mount',   ('in', ['ext3', 'ext4']), ('=', ('ro', 'rbind')),  '/a',   
        '/b',           False, False, False, ' #cmt')),
+        ('mount fstype in (ext3, ext4) option in (ro, rbind) /a, #cmt',     
MountRule('mount',   ('in', ['ext3', 'ext4']), ('in', ('ro', 'rbind')), '/a',   
        MountRule.ALL,  False, False, False, ' #cmt')),
+        ('mount fstype=(ext3, ext4) option=(ro, rbind) /a -> /b, #cmt',     
MountRule('mount',   ('=', ['ext3', 'ext4']),  ('=', ('ro', 'rbind')),  '/a',   
        '/b',           False, False, False, ' #cmt')),
         ('mount options=(rw, rbind) {,/usr}/lib{,32,64,x32}/modules/ -> 
/tmp/snap.rootfs_*{,/usr}/lib/modules/,',
                                                                             
MountRule('mount',   MountRule.ALL,            ('=', ('rw', 'rbind')),  
'{,/usr}/lib{,32,64,x32}/modules/',
                                                                                
                                                                                
    '/tmp/snap.rootfs_*{,/usr}/lib/modules/',
                                                                                
                                                                                
                     False, False, False, ''     )),
         ('umount,',                                                         
MountRule('umount',  MountRule.ALL,            MountRule.ALL,           
MountRule.ALL,  MountRule.ALL,  False, False, False, ''     )),
-        ('umount fstype=ext3,',                                             
MountRule('umount',  ('=', ('ext3')),          MountRule.ALL,           
MountRule.ALL,  MountRule.ALL,  False, False, False, ''     )),
+        ('umount fstype=ext3,',                                             
MountRule('umount',  ('=', ['ext3']),          MountRule.ALL,           
MountRule.ALL,  MountRule.ALL,  False, False, False, ''     )),
         ('umount /a,',                                                      
MountRule('umount',  MountRule.ALL,            MountRule.ALL,           
MountRule.ALL,  '/a',           False, False, False, ''     )),
 
         ('remount,',                                                        
MountRule('remount', MountRule.ALL,            MountRule.ALL,           
MountRule.ALL,  MountRule.ALL,  False, False, False, ''     )),
-        ('remount fstype=ext4,',                                            
MountRule('remount', ('=', ('ext4')),          MountRule.ALL,           
MountRule.ALL,  MountRule.ALL,  False, False, False, ''     )),
+        ('remount fstype=ext4,',                                            
MountRule('remount', ('=', ['ext4']),          MountRule.ALL,           
MountRule.ALL,  MountRule.ALL,  False, False, False, ''     )),
         ('remount /b,',                                                     
MountRule('remount', MountRule.ALL,            MountRule.ALL,           
MountRule.ALL,  '/b',           False, False, False, ''     )),
     )
 
@@ -72,7 +72,6 @@ class MountTestParse(AATest):
 class MountTestParseInvalid(AATest):
     tests = (
         ('mount fstype=,',           AppArmorException),
-        ('mount fstype=(foo),',      AppArmorException),
         ('mount fstype=(),',         AppArmorException),
         ('mount options=(),',        AppArmorException),
         ('mount option=(invalid),',  AppArmorException),
@@ -90,7 +89,7 @@ class MountTestParseInvalid(AATest):
 
     def test_diff_non_mountrule(self):
         exp = namedtuple('exp', ('audit', 'deny'))
-        obj = MountRule('mount', ('=', 'ext4'), MountRule.ALL, MountRule.ALL, 
MountRule.ALL)
+        obj = MountRule('mount', ('=', ['ext4']), MountRule.ALL, 
MountRule.ALL, MountRule.ALL)
         with self.assertRaises(AppArmorBug):
             obj.is_equal(exp(False, False), False)
 
@@ -98,9 +97,25 @@ class MountTestParseInvalid(AATest):
         with self.assertRaises(AppArmorBug):
             MountRule('mount', ('ext3', 'ext4'), MountRule.ALL, MountRule.ALL, 
MountRule.ALL)  # fstype[0] should be '=' or 'in'
 
-    def test_diff_invalid_fstype_keyword(self):
-        with self.assertRaises(AppArmorException):
-            MountRule('mount', ('=', 'invalidfs'), MountRule.ALL, 
MountRule.ALL, MountRule.ALL)  # fstype[0] should be '=' or 'in'
+    def test_diff_invalid_fstype_aare(self):
+        tests = [
+                'mount fstype=({unclosed_regex),',
+                'mount fstype=({closed}twice}),',
+        ]
+
+        for t in tests:
+            with self.assertRaises(AppArmorException):
+                MountRule.create_instance(t)
+
+    def test_diff_invalid_fstype_aare_2(self):
+        fslists = [
+                ['invalid_{_regex'],
+                ['ext4', 'invalid_}_regex'],
+                ['ext4', '{invalid} {regex}']
+        ]
+        for fslist in fslists:
+            with self.assertRaises(AppArmorException):
+                MountRule('mount', ('=', fslist), MountRule.ALL, 
MountRule.ALL, MountRule.ALL)
 
     def test_diff_invalid_options_equals_or_in(self):
         with self.assertRaises(AppArmorBug):
@@ -111,7 +126,7 @@ class MountTestParseInvalid(AATest):
             MountRule('mount', MountRule.ALL, ('=', 'invalid'), MountRule.ALL, 
MountRule.ALL)  # fstype[0] should be '=' or 'in'
 
     def test_diff_fstype(self):
-        obj1 = MountRule('mount', ('=', 'ext4'), MountRule.ALL, MountRule.ALL, 
MountRule.ALL)
+        obj1 = MountRule('mount', ('=', ['ext4']), MountRule.ALL, 
MountRule.ALL, MountRule.ALL)
         obj2 = MountRule('mount', MountRule.ALL, MountRule.ALL, MountRule.ALL, 
MountRule.ALL)
         self.assertFalse(obj1.is_equal(obj2, False))
 
@@ -129,14 +144,6 @@ class MountTestParseInvalid(AATest):
             MountRule('remount', MountRule.ALL, MountRule.ALL, '/foo', 
MountRule.ALL)
 
 
-class MountTestFilesystems(AATest):
-    def test_fs(self):
-        with open('/proc/filesystems') as f:
-            for line in f:
-                fs_name = line.split()[-1]
-                self.assertTrue(fs_name in valid_fs, '/proc/filesystems 
contains %s which is not listed in MountRule valid_fs' % fs_name)
-
-
 class MountTestGlob(AATest):
     def test_glob(self):
         globList = [(
@@ -199,49 +206,58 @@ class MountIsCoveredTest(AATest):
     def test_is_covered(self):
         obj = MountRule('mount', ('=', ('ext3', 'ext4')), ('=', ('ro')), 
'/foo/b*', '/b*')
         tests = [
-            ('mount', ('=', ('ext3', 'ext4')), ('=', ('ro')), '/foo/b', 
'/bar'),
-            ('mount', ('=', ('ext3', 'ext4')), ('=', ('ro')), '/foo/bar', '/b')
+            ('mount', ('=', ['ext3', 'ext4']), ('=', ('ro')), '/foo/b', 
'/bar'),
+            ('mount', ('=', ['ext3', 'ext4']), ('=', ('ro')), '/foo/bar', '/b')
         ]
         for test in tests:
             self.assertTrue(obj.is_covered(MountRule(*test)))
             self.assertFalse(obj.is_equal(MountRule(*test)))
 
     def test_is_covered_fs_source(self):
-        obj = MountRule('mount', ('=', ('ext3', 'ext4')), ('=', ('ro')), 
'tmpfs', MountRule.ALL)
-        self.assertTrue(obj.is_covered(MountRule('mount', ('=', ('ext3')), 
('=', ('ro')), 'tmpfs', MountRule.ALL)))
-        self.assertFalse(obj.is_equal(MountRule('mount', ('=', ('ext3')), 
('=', ('ro')), 'tmpfs', MountRule.ALL)))
+        obj = MountRule('mount', ('=', ['ext3', 'ext4']), ('=', ('ro')), 
'tmpfs', MountRule.ALL)
+        self.assertTrue(obj.is_covered(MountRule('mount', ('=', ['ext3']), 
('=', ('ro')), 'tmpfs', MountRule.ALL)))
+        self.assertFalse(obj.is_equal(MountRule('mount', ('=', ['ext3']), 
('=', ('ro')), 'tmpfs', MountRule.ALL)))
 
-    def test_is_covered_regex(self):
-        obj = MountRule('mount', ('=', ('sys*', 'fuse.*')), ('=', ('ro')), 
'tmpfs', MountRule.ALL)
+    def test_is_covered_aare_1(self):
+        obj = MountRule('mount', ('=', ['sys*', 'fuse.*']), ('=', ('ro')), 
'tmpfs', MountRule.ALL)
         tests = [
-            ('mount', ('=', ('sysfs', 'fuse.s3fs')), ('=', ('ro')), 'tmpfs', 
MountRule.ALL),
-            ('mount', ('=', ('sysfs', 'fuse.jmtpfs', 'fuse.s3fs', 
'fuse.obexfs', 'fuse.obexautofs', 'fuse.fuseiso')), ('=', ('ro')), 'tmpfs', 
MountRule.ALL)
+            ('mount', ('=', ['sysfs', 'fuse.s3fs']), ('=', ('ro')), 'tmpfs', 
MountRule.ALL),
+            ('mount', ('=', ['sysfs', 'fuse.jmtpfs', 'fuse.s3fs', 
'fuse.obexfs', 'fuse.obexautofs', 'fuse.fuseiso']), ('=', ('ro')), 'tmpfs', 
MountRule.ALL)
+        ]
+        for test in tests:
+            self.assertTrue(obj.is_covered(MountRule(*test)))
+            self.assertFalse(obj.is_equal(MountRule(*test)))
+    def test_is_covered_aare_2(self):
+        obj = MountRule('mount', ('=', ['ext{3,4}', '{cgroup*,fuse.*}']), 
('=', ('ro')), 'tmpfs', MountRule.ALL)
+        tests = [
+            ('mount', ('=', ['ext3']), ('=', ('ro')), 'tmpfs', MountRule.ALL),
+            ('mount', ('=', ['ext3', 'ext4', 'cgroup', 'cgroup2', 
'fuse.jmtpfs', 'fuse.s3fs', 'fuse.obexfs', 'fuse.obexautofs', 'fuse.fuseiso']), 
('=', ('ro')), 'tmpfs', MountRule.ALL)
         ]
         for test in tests:
             self.assertTrue(obj.is_covered(MountRule(*test)))
             self.assertFalse(obj.is_equal(MountRule(*test)))
 
     def test_is_notcovered(self):
-        obj = MountRule('mount', ('=', ('ext3', 'ext4')), ('=', ('ro')), 
'/foo/b*', '/b*')
+        obj = MountRule('mount', ('=', ['ext3', 'ext4']), ('=', ('ro')), 
'/foo/b*', '/b*')
         tests = [
-            ('mount',   ('in', ('ext3', 'ext4')),   ('=', ('ro')), '/foo/bar', 
    '/bar'    ),
-            ('mount',   ('=', ('procfs', 'ext4')),  ('=', ('ro')), '/foo/bar', 
    '/bar'    ),
-            ('mount',   ('=', ('ext3')),            ('=', ('rw')), '/foo/bar', 
    '/bar'    ),
-            ('mount',   ('=', ('ext3', 'ext4')),    MountRule.ALL, '/foo/b*',  
    '/bar'    ),
+            ('mount',   ('in', ['ext3', 'ext4']),   ('=', ('ro')), '/foo/bar', 
    '/bar'    ),
+            ('mount',   ('=', ['procfs', 'ext4']),  ('=', ('ro')), '/foo/bar', 
    '/bar'    ),
+            ('mount',   ('=', ['ext3']),            ('=', ('rw')), '/foo/bar', 
    '/bar'    ),
+            ('mount',   ('=', ['ext3', 'ext4']),    MountRule.ALL, '/foo/b*',  
    '/bar'    ),
             ('mount',   MountRule.ALL,              ('=', ('ro')), '/foo/b*',  
    '/bar'    ),
-            ('mount',   ('=', ('ext3', 'ext4')),    ('=', ('ro')), 
'/invalid/bar', '/bar'    ),
+            ('mount',   ('=', ['ext3', 'ext4']),    ('=', ('ro')), 
'/invalid/bar', '/bar'    ),
             ('umount',  MountRule.ALL,              MountRule.ALL, 
MountRule.ALL,  '/bar'    ),
             ('remount', MountRule.ALL,              MountRule.ALL, 
MountRule.ALL,  '/bar'    ),
-            ('mount',   ('=', ('ext3', 'ext4')),    ('=', ('ro')), 'tmpfs',    
    '/bar'    ),
-            ('mount',   ('=', ('ext3', 'ext4')),    ('=', ('ro')), '/foo/b*',  
    '/invalid'),
+            ('mount',   ('=', ['ext3', 'ext4']),    ('=', ('ro')), 'tmpfs',    
    '/bar'    ),
+            ('mount',   ('=', ['ext3', 'ext4']),    ('=', ('ro')), '/foo/b*',  
    '/invalid'),
         ]
         for test in tests:
             self.assertFalse(obj.is_covered(MountRule(*test)))
             self.assertFalse(obj.is_equal(MountRule(*test)))
 
     def test_is_not_covered_fs_source(self):
-        obj = MountRule('mount', ('=', ('ext3', 'ext4')), ('=', ('ro')), 
'tmpfs', MountRule.ALL)
-        test = ('mount', ('=', ('ext3', 'ext4')), ('=', ('ro')), 'procfs', 
MountRule.ALL)
+        obj = MountRule('mount', ('=', ['ext3', 'ext4']), ('=', ('ro')), 
'tmpfs', MountRule.ALL)
+        test = ('mount', ('=', ['ext3', 'ext4']), ('=', ('ro')), 'procfs', 
MountRule.ALL)
         self.assertFalse(obj.is_covered(MountRule(*test)))
         self.assertFalse(obj.is_equal(MountRule(*test)))
 
diff --git a/utils/test/test-parser-simple-tests.py 
b/utils/test/test-parser-simple-tests.py
index 40f61ef85..451af7d22 100644
--- a/utils/test/test-parser-simple-tests.py
+++ b/utils/test/test-parser-simple-tests.py
@@ -324,9 +324,6 @@ unknown_line = (
     'bare_include_tests/ok_85.sd',
     'bare_include_tests/ok_86.sd',
 
-    # mount with fstype using AARE
-    'mount/ok_12.sd',
-
     # Mount with flags in {remount, [r]unbindable, [r]shared, [r]private, 
[r]slave} does not support a source
     'mount/ok_opt_68.sd',
     'mount/ok_opt_69.sd',

Reply via email to