[apparmor] [patch] Implement in-profile de-duplication in BaseRuleset

Christian Boltz apparmor at cboltz.de
Sun Apr 12 01:29:44 UTC 2015


Hello,

this patch implements in-profile de-duplication in BaseRuleset 
(currently affects "only" CapabilityRuleset, but will also work for all 
future *Ruleset classes).

The method I use is probably slightly confusing, but it works ;-)

(1) Store the current rules in oldrules, empty self.rules and then
    self.is_covered()-loop over oldrules and add only the needed rules
    back to self.rules. After this step, some, but not all duplicates
    are removed (depends on the rule order inside the profile file).

(2) Reverse the order of self.rules

(3) Do step (1) again. This deletes the remaining duplicates.

(3) Reverse self.rules again to get the original order of the
    (remaining) rules back.

If you are still confused, look at the examples below ;-)


The patch also adds some tests that verify the in-profile deduplication.



Here are the examples showing how/in which step superflous rules are
deleted. (Scroll down for the patch ;-)

---------------------------------------

/bin/foo_1 {
   audit capability chown,
   audit capability,
   audit capability dac_override,
   capability ,
   audit capability ,
}

*** delete_duplicates (1) ***
KEEP   audit capability chown,
KEEP   audit capability,
DELETE audit capability dac_override,
DELETE capability,
DELETE audit capability,
*** delete_duplicates (3) ***
KEEP   audit capability,
DELETE audit capability chown,

---------------------------------------

/bin/foo_2 {
   audit capability chown,
   audit capability,
}

*** delete_duplicates (1) ***
KEEP   audit capability chown,
KEEP   audit capability,
*** delete_duplicates (3) ***
KEEP   audit capability,
DELETE audit capability chown,

---------------------------------------

/bin/foo_3 {
   audit capability chown,
   capability dac_override,
   deny capability dac_override,
   capability dac_override,
   audit capability chown,
   deny capability chown,
   audit deny capability chown,
   capability,
   audit capability,
}

*** delete_duplicates (1) ***
KEEP   audit capability chown,
KEEP   capability dac_override,
KEEP   deny capability dac_override,
DELETE capability dac_override,
DELETE audit capability chown,
KEEP   deny capability chown,
KEEP   audit deny capability chown,
KEEP   capability,
KEEP   audit capability,
*** delete_duplicates (3) ***
KEEP   audit capability,
DELETE capability,
KEEP   audit deny capability chown,
DELETE deny capability chown,
KEEP   deny capability dac_override,
DELETE capability dac_override,
DELETE audit capability chown,

---------------------------------------



[ 42-in-profile-deduplication.diff ]

=== modified file 'utils/apparmor/rule/__init__.py'
--- utils/apparmor/rule/__init__.py     2015-01-16 13:59:49 +0000
+++ utils/apparmor/rule/__init__.py     2015-04-12 01:15:34 +0000
@@ -227,14 +230,40 @@
 
     def delete_duplicates(self, include_rules):
         '''Delete duplicate rules.
-           include_rules must be a *_rules object'''
+           include_rules must be a *_rules object or None'''
+
         deleted = []
-        if include_rules:  # avoid breakage until we have a proper function to ensure all profiles contain all *_rules objects
+
+        # delete rules that are covered by include files
+        if include_rules:
             for rule in self.rules:
                 if include_rules.is_covered(rule, True, True):
                     self.delete(rule)
                     deleted.append(rule)
 
+        num_deleted = len(deleted)
+
+        # de-duplicate rules inside the profile
+        num_deleted += self.delete_in_profile_duplicates()
+        self.rules.reverse()
+        num_deleted += self.delete_in_profile_duplicates()  # search again in reverse order - this will find more duplicates
+        self.rules.reverse()  # restore original order for raw output
+
+        return num_deleted
+
+    def delete_in_profile_duplicates(self):
+        '''Delete duplicate rules inside a profile'''
+
+        deleted = []
+        oldrules = self.rules
+        self.rules = []
+
+        for rule in oldrules:
+            if not self.is_covered(rule, True, False):
+                self.rules.append(rule)
+            else:
+                deleted.append(rule)
+
         return len(deleted)
 
     def get_glob_ext(self, path_or_rule):

=== modified file 'utils/test/test-capability.py'
--- utils/test/test-capability.py       2015-02-28 13:09:45 +0000
+++ utils/test/test-capability.py       2015-04-12 00:20:33 +0000
@@ -858,5 +864,95 @@
         self.assertEqual(expected_clean, self.ruleset.get_clean(1))
 
 
+    def _check_test_delete_duplicates_in_profile(self, rules, expected_raw, expected_clean, expected_deleted):
+        obj = CapabilityRuleset()
+
+        for rule in rules:
+            obj.add(CapabilityRule.parse(rule))
+
+        deleted = obj.delete_duplicates(None)
+
+        self.assertEqual(expected_raw, obj.get_raw(1))
+        self.assertEqual(expected_clean, obj.get_clean(1))
+        self.assertEqual(deleted, expected_deleted)
+
+
+    def test_delete_duplicates_in_profile_01(self):
+        rules = [
+            'audit capability chown,',
+            'audit capability,',
+            'capability dac_override,',
+        ]
+
+        expected_raw = [
+            '  audit capability,',
+            '',
+        ]
+
+        expected_clean = [
+            '  audit capability,',
+            '',
+        ]
+
+        expected_deleted = 2
+
+        self._check_test_delete_duplicates_in_profile(rules, expected_raw, expected_clean, expected_deleted)
+
+    def test_delete_duplicates_in_profile_02(self):
+        rules = [
+            'audit capability chown,',
+            'audit capability,',
+            'audit capability dac_override,',
+            'capability ,',
+            'audit capability ,',
+        ]
+
+        expected_raw = [
+            '  audit capability,',
+            '',
+        ]
+
+        expected_clean = [
+            '  audit capability,',
+            '',
+        ]
+
+        expected_deleted = 4
+
+        self._check_test_delete_duplicates_in_profile(rules, expected_raw, expected_clean, expected_deleted)
+
+    def test_delete_duplicates_in_profile_03(self):
+        rules = [
+            'audit capability chown,',
+            'capability dac_override,',
+            'deny capability dac_override,',
+            'capability dac_override,',
+            'audit capability chown,',
+            'deny capability chown,',
+            'audit deny capability chown,',
+            'capability,',
+            'audit capability,',
+        ]
+
+        expected_raw = [
+            '  deny capability dac_override,',
+            '  audit deny capability chown,',
+            '  audit capability,',
+            '',
+        ]
+
+        expected_clean = [
+            '  audit deny capability chown,',
+            '  deny capability dac_override,',
+            '',
+            '  audit capability,',
+            '',
+        ]
+
+        expected_deleted = 6
+
+        self._check_test_delete_duplicates_in_profile(rules, expected_raw, expected_clean, expected_deleted)
+
+
 if __name__ == "__main__":
     unittest.main(verbosity=2)




Regards,

Christian Boltz
-- 
[Debian] für meinen privaten Rechner ist es mir zu konservativ.
Da bastel ich gerne mal und wälze mich wollüstig in Featureitis
und Versionismus und bin erst glücklich, wenn das System ernst-
lich Schaden genommen hat.                [Ratti in suse-linux]




More information about the AppArmor mailing list