Hello,

Am Montag, 15. Juni 2015 schrieb Kshitij Gupta:
> On Mon, Jun 15, 2015 at 12:44 AM, Christian Boltz wrote:
> > this patch moves re_match_include() to regex.py. The function is
> > basically a wrapper around a regex, so regex.py is a much better
> > home.
> > 
> > While on it, rename the regex to RE_INCLUDE and compile it outside
> > the function, which should result in a (small) performance
> > improvement.
> > 
> > Also adjust code calling it to the new location.
> > 
> > Finally, add some tests for re_match_include()
> > 
> > 
> > 
> > [ 49-move-re_match_include.diff ]

> > === modified file utils/apparmor/regex.py
> > --- utils/apparmor/regex.py     2015-06-14 20:33:26.205498791 +0200
> > +++ utils/apparmor/regex.py     2015-06-14 20:14:39.423202789 +0200
> > @@ -112,6 +112,19 @@
> > 
> >      return result
> > 
> > +
> > +RE_INCLUDE = re.compile('^\s*#?include\s*<(.*)>\s*(#.*)?$')
> 
> How about replacing the end part using RE_EOL?

Initially I just wanted to move the function, but... ;-)

That said - good idea, and the tests added by this patch still pass 
after switching to RE_EOL.

I also switched to named matches In case you wonder why I use the name
'magicpath' - that's what's used in man apparmor.d for <...> style 
includes. There's also the "include foo" variant which behaves 
differently, but the tools don't support that yet.

> Also the .* for include name means we can have #include<>? I would
> believe the check happens somewhere for valid paths this but why not
> deal with the easy case in regex itself?

That would effectively mean that   #include <>   will be handled as a 
comment (and silently ignored), not as syntax error. That doesn't sound like a 
good idea.

You have a good point that we should check for an empty filename -
I added that in re_match_include() and also added some tests for it.

> Also, about should the regex be placed directly above the function
> using it or along with all the other regexes?

I prefer to have it next to the function - even if I didn't always do 
this ;-)  (RE_PROFILE_CHANGE_PROFILE managed to get between 
RE_PROFILE_START and parse_profile_start_line() :-/ )

> > +def re_match_include(path):
> > +    """Matches the path for include and returns the include path"""
> > +    match = RE_INCLUDE.search(path)
> > +    if match:
> > +        return match.groups()[0]
> 
> hmm... for the matters of this function the second comment group in
> regex is wasted.

The whole comment group is optional, so we need to wrap it - it's hard 
or even impossible to rewrite (#.*)? to something without (...) ;-)

> +    else:
> > +        return None
> > +
> > +
> > +
> 
> Bikeshedding: 3 newlines or 2 newlines for separation?

2.6, and do the rounding depending on the moon phase ;-)

Seriously: I don't have a strict rule in my head besides "whatever looks 
good", but 2 are enough here.

> > === modified file utils/test/test-regex_matches.py
> > 
> > +        ('   #include    <abstractions/base>  ',   
> > 'abstractions/base'> 
> >        ),
> 
> could probably also use #include<abstractions/base> the no space
> version?

Good idea, added.

> > +    def _run_test(self, params, expected):
> > +        self.assertEqual(re_match_include(params), expected)
> > +
> 
> You could probably add just copy-paste (with little tweaking) the
> above tests with the regex instead of the function and cover the
> comment part.

Nothing is expected to use the regex directly, so I don't see a need to 
explicitely test it ;-)

> >  class TestStripQuotes(AATest):
> >      def test_strip_quotes_01(self):
> With suggestions for regex and tests addressed.
> 
> Acked-by: Kshitij Gupta <kgupta8...@gmail.com>.

I did quite some changes (as described above) which are nearly a rewrite 
of re_match_include(), so please check the patch or at least the 
interdiff[1] again ;-)



Here's the updated patch:


Move re_match_include() to regex.py and improve it

The function is basically a wrapper around a regex, so regex.py is a
much better home.

While on it, rename the regex to RE_INCLUDE, change it to named matches,
use RE_EOL to handle comments and compile it outside the function, which
should result in a (small) performance improvement.

Also rewrite re_match_include() and let it check for empty include
filenames ("#include <>") and let it raise AppArmorException in that case.

Finally, adjust code calling it to the new location, and add some tests
for re_match_include()



[ 49-move-re_match_include.diff ]

=== modified file utils/aa-mergeprof
--- utils/aa-mergeprof  2015-06-18 23:00:46.789404817 +0200
+++ utils/aa-mergeprof  2015-06-14 20:13:24.204740154 +0200
@@ -17,9 +17,10 @@
 import os
 
 import apparmor.aa
-from apparmor.aa import available_buttons, combine_name, delete_duplicates, 
is_known_rule, match_includes, re_match_include
+from apparmor.aa import available_buttons, combine_name, delete_duplicates, 
is_known_rule, match_includes
 import apparmor.aamode
 from apparmor.common import AppArmorException
+from apparmor.regex import re_match_include
 import apparmor.severity
 import apparmor.cleanprofile as cleanprofile
 import apparmor.ui as aaui
@@ -267,7 +268,7 @@
                 done = True
             elif ans == 'CMD_ALLOW':
                 selection = options[selected]
-                inc = apparmor.aa.re_match_include(selection)
+                inc = re_match_include(selection)
                 self.user.filelist[self.user.filename]['include'][inc] = True
                 options.pop(selected)
                 aaui.UI_Info(_('Adding %s to the file.') % selection)
@@ -302,7 +303,7 @@
                     done = True
                 elif ans == 'CMD_ALLOW':
                     selection = options[selected]
-                    inc = apparmor.aa.re_match_include(selection)
+                    inc = re_match_include(selection)
                     deleted = apparmor.aa.delete_duplicates(aa[profile][hat], 
inc)
                     aa[profile][hat]['include'][inc] = True
                     options.pop(selected)
@@ -524,7 +525,7 @@
                             elif ans == 'CMD_ALLOW':
                                 path = options[selected]
                                 done = True
-                                match = apparmor.aa.re_match_include(path)
+                                match = re_match_include(path)
                                 if match:
                                     inc = match
                                     deleted = 
apparmor.aa.delete_duplicates(aa[profile][hat], inc)
@@ -589,7 +590,7 @@
 
                             elif ans == 'CMD_NEW':
                                 arg = options[selected]
-                                if not apparmor.aa.re_match_include(arg):
+                                if not re_match_include(arg):
                                     ans = aaui.UI_GetString(_('Enter new path: 
'), arg)
 #                                         if ans:
 #                                             if not matchliteral(ans, path):
@@ -603,7 +604,7 @@
 
                             elif ans == 'CMD_GLOB':
                                 newpath = options[selected].strip()
-                                if not apparmor.aa.re_match_include(newpath):
+                                if not re_match_include(newpath):
                                     newpath = apparmor.aa.glob_path(newpath)
 
                                     if newpath not in options:
@@ -614,7 +615,7 @@
 
                             elif ans == 'CMD_GLOBEXT':
                                 newpath = options[selected].strip()
-                                if not apparmor.aa.re_match_include(newpath):
+                                if not re_match_include(newpath):
                                     newpath = 
apparmor.aa.glob_path_withext(newpath)
 
                                     if newpath not in options:
=== modified file utils/apparmor/aa.py
--- utils/apparmor/aa.py        2015-06-18 23:00:46.791404710 +0200
+++ utils/apparmor/aa.py        2015-06-15 00:22:30.094916147 +0200
@@ -49,7 +49,7 @@
                             RE_PROFILE_HAT_DEF, RE_PROFILE_DBUS, 
RE_PROFILE_MOUNT,
                             RE_PROFILE_SIGNAL, RE_PROFILE_PTRACE, 
RE_PROFILE_PIVOT_ROOT,
                             RE_PROFILE_UNIX, RE_RULE_HAS_COMMA, 
RE_HAS_COMMENT_SPLIT,
-                            strip_quotes, parse_profile_start_line )
+                            strip_quotes, parse_profile_start_line, 
re_match_include )
 
 import apparmor.rules as aarules
 
@@ -2110,15 +2110,6 @@
 
     return newincludes
 
-def re_match_include(path):
-    """Matches the path for include and returns the include path"""
-    regex_include = re.compile('^\s*#?include\s*<(.*)>\s*(#.*)?$')
-    match = regex_include.search(path)
-    if match:
-        return match.groups()[0]
-    else:
-        return None
-
 def valid_include(profile, incname):
     if profile and profile['include'].get(incname, False):
         return False
=== modified file utils/apparmor/cleanprofile.py
--- utils/apparmor/cleanprofile.py      2015-06-18 23:00:46.808403808 +0200
+++ utils/apparmor/cleanprofile.py      2015-06-14 20:29:20.957207783 +0200
@@ -12,6 +12,7 @@
 #
 # ----------------------------------------------------------------------
 import apparmor.aa as apparmor
+from apparmor.regex import re_match_include
 
 class Prof(object):
     def __init__(self, filename):
@@ -90,7 +91,7 @@
                     if not same_profile:
                         deleted.append(entry)
                 continue
-            if apparmor.re_match_include(rule) or 
apparmor.re_match_include(entry):
+            if re_match_include(rule) or re_match_include(entry):
                 continue
             # Check if the rule implies entry
             if apparmor.matchliteral(rule, entry):
=== modified file utils/apparmor/regex.py
--- utils/apparmor/regex.py     2015-06-18 23:00:46.808403808 +0200
+++ utils/apparmor/regex.py     2015-06-18 23:28:06.825232112 +0200
@@ -112,6 +112,21 @@
     return result
 
 
+RE_INCLUDE = re.compile('^\s*#?include\s*<(?P<magicpath>.*)>' + RE_EOL)
+
+def re_match_include(line):
+    """Matches the path for include and returns the include path"""
+    matches = RE_INCLUDE.search(line)
+
+    if not matches:
+        return None
+
+    if not matches.group('magicpath').strip():
+        raise AppArmorException(_('Syntax error: #include rule with empty 
filename'))
+
+    return matches.group('magicpath')
+
+
 def strip_quotes(data):
     if data[0] + data[-1] == '""':
         return data[1:-1]
=== modified file utils/test/test-regex_matches.py
--- utils/test/test-regex_matches.py    2015-06-18 23:00:46.808403808 +0200
+++ utils/test/test-regex_matches.py    2015-06-18 23:22:09.054007926 +0200
@@ -12,9 +12,9 @@
 import apparmor.aa as aa
 import unittest
 from common_test import AATest, setup_all_loops
-from apparmor.common import AppArmorBug
+from apparmor.common import AppArmorBug, AppArmorException
 
-from apparmor.regex import strip_quotes, parse_profile_start_line, 
RE_PROFILE_START, RE_PROFILE_CAP
+from apparmor.regex import strip_quotes, parse_profile_start_line, 
re_match_include, RE_PROFILE_START, RE_PROFILE_CAP
 
 
 class AARegexTest(AATest):
@@ -465,6 +465,34 @@
         with self.assertRaises(AppArmorBug):
             parse_profile_start_line(line, 'somefile')
 
+class Test_re_match_include(AATest):
+    tests = [
+        ('#include <abstractions/base>',            'abstractions/base'        
 ),
+        ('#include <abstractions/base> # comment',  'abstractions/base'        
 ),
+        ('#include<abstractions/base>#comment',     'abstractions/base'        
 ),
+        ('   #include    <abstractions/base>  ',    'abstractions/base'        
 ),
+        ('include <abstractions/base>',             'abstractions/base'        
 ), # not supported by parser
+        # ('include foo',                           'foo'                      
 ), # XXX not supported in tools yet
+        # ('include /foo/bar',                      '/foo/bar'                 
 ), # XXX not supported in tools yet
+        # ('include "foo"',                         'foo'                      
 ), # XXX not supported in tools yet
+        # ('include "/foo/bar"',                    '/foo/bar'                 
 ), # XXX not supported in tools yet
+        (' some #include <abstractions/base>',      None,                      
 ),
+        ('  /etc/fstab r,',                         None,                      
 ),
+    ]
+
+    def _run_test(self, params, expected):
+        self.assertEqual(re_match_include(params), expected)
+
+class TestInvalid_re_match_include(AATest):
+    tests = [
+        ('#include <>',                             AppArmorException   ),
+        ('#include <  >',                           AppArmorException   ),
+    ]
+
+    def _run_test(self, params, expected):
+        with self.assertRaises(expected):
+            re_match_include(params)
+
 
 class TestStripQuotes(AATest):
     def test_strip_quotes_01(self):



Regards,

Christian Boltz


[1] interdiff 49-move-re_match_include.OLD 49-move-re_match_include.diff 
diff -u utils/apparmor/regex.py utils/apparmor/regex.py
--- utils/apparmor/regex.py     2015-06-14 20:14:39.423202789 +0200
+++ utils/apparmor/regex.py     2015-06-18 23:28:06.825232112 +0200
@@ -112,17 +112,19 @@
     return result
 
 
+RE_INCLUDE = re.compile('^\s*#?include\s*<(?P<magicpath>.*)>' + RE_EOL)
 
-RE_INCLUDE = re.compile('^\s*#?include\s*<(.*)>\s*(#.*)?$')
-
-def re_match_include(path):
+def re_match_include(line):
     """Matches the path for include and returns the include path"""
-    match = RE_INCLUDE.search(path)
-    if match:
-        return match.groups()[0]
-    else:
+    matches = RE_INCLUDE.search(line)
+
+    if not matches:
         return None
 
+    if not matches.group('magicpath').strip():
+        raise AppArmorException(_('Syntax error: #include rule with empty 
filename'))
+
+    return matches.group('magicpath')
 
 
 def strip_quotes(data):
diff -u utils/test/test-regex_matches.py utils/test/test-regex_matches.py
--- utils/test/test-regex_matches.py    2015-06-14 20:14:57.120135248 +0200
+++ utils/test/test-regex_matches.py    2015-06-18 23:22:09.054007926 +0200
@@ -12,7 +12,7 @@
 import apparmor.aa as aa
 import unittest
 from common_test import AATest, setup_all_loops
-from apparmor.common import AppArmorBug
+from apparmor.common import AppArmorBug, AppArmorException
 
 from apparmor.regex import strip_quotes, parse_profile_start_line, 
re_match_include, RE_PROFILE_START, RE_PROFILE_CAP
 
@@ -469,6 +469,7 @@
     tests = [
         ('#include <abstractions/base>',            'abstractions/base'        
 ),
         ('#include <abstractions/base> # comment',  'abstractions/base'        
 ),
+        ('#include<abstractions/base>#comment',     'abstractions/base'        
 ),
         ('   #include    <abstractions/base>  ',    'abstractions/base'        
 ),
         ('include <abstractions/base>',             'abstractions/base'        
 ), # not supported by parser
         # ('include foo',                           'foo'                      
 ), # XXX not supported in tools yet
@@ -482,6 +483,16 @@
     def _run_test(self, params, expected):
         self.assertEqual(re_match_include(params), expected)
 
+class TestInvalid_re_match_include(AATest):
+    tests = [
+        ('#include <>',                             AppArmorException   ),
+        ('#include <  >',                           AppArmorException   ),
+    ]
+
+    def _run_test(self, params, expected):
+        with self.assertRaises(expected):
+            re_match_include(params)
+
 
 class TestStripQuotes(AATest):
     def test_strip_quotes_01(self):

-- 
Spätestens dabei handelt es sich um Filtereffekte, die ImageMagick
bestimmt nicht beherrschen kann. Sollten sie _das_ nachprogrammiert
haben, würde ich barfuß hinlaufen und ihnen ein halbes Schwein
opfern ob ihrer Genialität. [Ratti in suse-linux]


-- 
AppArmor mailing list
AppArmor@lists.ubuntu.com
Modify settings or unsubscribe at: 
https://lists.ubuntu.com/mailman/listinfo/apparmor

Reply via email to