2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-08-29 05:17:59 +00:00
apparmor/utils/test/test-aare.py
Christian Boltz dde4609f22 [28/38] AARE: let match() handle plain path regexes as non-regex
When matching an AARE against another AARE, most AARE objects don't
contain orig_regex (only AARE instances originating from a log event
contain orig_regex).

In this case, match() will use is_equal() to error out on the safe side.
Unfortunately this also means that there are lots of false negative
cases where match() returns False errornously.

With this patch, match() checks the given AARE regex and, if it doesn't
contain any special characters (wildcards, alternations or variables),
handles it as plain path. This avoids most of the false negatives.

Also extend the AARE tests to check a bunch of plain path regexes using
AARE matching instead of only str matching.



Acked-by: Steve Beattie <steve@nxnw.org>
2016-10-01 20:07:17 +02:00

364 lines
16 KiB
Python

#! /usr/bin/env python
# ------------------------------------------------------------------
#
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
# Copyright (C) 2015 Christian Boltz <apparmor@cboltz.de>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
# License published by the Free Software Foundation.
#
# ------------------------------------------------------------------
import unittest
from common_test import AATest, setup_all_loops
from copy import deepcopy
import re
from apparmor.common import convert_regexp, AppArmorBug, AppArmorException
from apparmor.aare import AARE, convert_expression_to_aare
class TestConvert_regexp(AATest):
tests = [
('/foo', '^/foo$'),
('/{foo,bar}', '^/(foo|bar)$'),
# ('/\{foo,bar}', '^/\{foo,bar}$'), # XXX gets converted to ^/\(foo|bar)$
('/fo[abc]', '^/fo[abc]$'),
('/foo bar', '^/foo bar$'),
('/x\y', '^/x\y$'),
('/x\[y', '^/x\[y$'),
('/x\\y', '^/x\\y$'),
('/fo?', '^/fo[^/\000]$'),
('/foo/*', '^/foo/(((?<=/)[^/\000]+)|((?<!/)[^/\000]*))$'),
('/foo/**.bar', '^/foo/(((?<=/)[^\000]+)|((?<!/)[^\000]*))\.bar$'),
]
def _run_test(self, params, expected):
self.assertEqual(convert_regexp(params), expected)
class Test_convert_expression_to_aare(AATest):
tests = [
# note that \ always needs to be escaped in python, so \\ is actually just \ in the string
('/foo', '/foo' ),
('/foo?', '/foo\\?' ),
('/foo*', '/foo\\*' ),
('/foo[bar]', '/foo\\[bar\\]' ),
('/foo{bar}', '/foo\\{bar\\}' ),
('/foo{', '/foo\\{' ),
('/foo\\', '/foo\\\\' ),
('/foo"', '/foo\\"' ),
('}]"\\[{', '\\}\\]\\"\\\\\\[\\{' ),
]
def _run_test(self, params, expected):
self.assertEqual(convert_expression_to_aare(params), expected)
class TestConvert_regexpAndAAREMatch(AATest):
tests = [
# aare path to check match expected?
(['/foo/**/bar/', '/foo/user/tools/bar/' ], True),
(['/foo/**/bar/', '/foo/apparmor/bar/' ], True),
(['/foo/**/bar/', '/foo/apparmor/bar' ], False),
(['/foo/**/bar/', '/a/foo/apparmor/bar/' ], False),
(['/foo/**/bar/', '/foo/apparmor/bar/baz' ], False),
(['/foo/*/bar/', '/foo/apparmor/bar/' ], True),
(['/foo/*/bar/', '/foo/apparmor/tools/bar/' ], False),
(['/foo/*/bar/', '/foo/apparmor/bar' ], False),
(['/foo/user/ba?/', '/foo/user/bar/' ], True),
(['/foo/user/ba?/', '/foo/user/bar/apparmor/' ], False),
(['/foo/user/ba?/', '/foo/user/ba/' ], False),
(['/foo/user/ba?/', '/foo/user/ba//' ], False),
(['/foo/user/bar/**', '/foo/user/bar/apparmor' ], True),
(['/foo/user/bar/**', '/foo/user/bar/apparmor/tools' ], True),
(['/foo/user/bar/**', '/foo/user/bar/' ], False),
(['/foo/user/bar/*', '/foo/user/bar/apparmor' ], True),
(['/foo/user/bar/*', '/foo/user/bar/apparmor/tools' ], False),
(['/foo/user/bar/*', '/foo/user/bar/' ], False),
(['/foo/user/bar/*', '/foo/user/bar/apparmor/' ], False),
(['/foo/**.jpg', '/foo/bar/baz/foobar.jpg' ], True),
(['/foo/**.jpg', '/foo/bar/foobar.jpg' ], True),
(['/foo/**.jpg', '/foo/bar/*.jpg' ], True),
(['/foo/**.jpg', '/foo/bar.jpg' ], True),
(['/foo/**.jpg', '/foo/**.jpg' ], True),
(['/foo/**.jpg', '/foo/*.jpg' ], True),
(['/foo/**.jpg', '/foo/barjpg' ], False),
(['/foo/**.jpg', '/foo/.*' ], False),
(['/foo/**.jpg', '/bar.jpg' ], False),
(['/foo/**.jpg', '/**.jpg' ], False),
(['/foo/**.jpg', '/*.jpg' ], False),
(['/foo/**.jpg', '/foo/*.bar' ], False),
(['/foo/{**,}', '/foo/' ], True),
(['/foo/{**,}', '/foo/bar' ], True),
(['/foo/{**,}', '/foo/bar/' ], True),
(['/foo/{**,}', '/foo/bar/baz' ], True),
(['/foo/{**,}', '/foo/bar/baz/' ], True),
(['/foo/{**,}', '/bar/' ], False),
(['/foo/{,**}', '/foo/' ], True),
(['/foo/{,**}', '/foo/bar' ], True),
(['/foo/{,**}', '/foo/bar/' ], True),
(['/foo/{,**}', '/foo/bar/baz' ], True),
(['/foo/{,**}', '/foo/bar/baz/' ], True),
(['/foo/{,**}', '/bar/' ], False),
(['/foo/a[bcd]e', '/foo/abe' ], True),
(['/foo/a[bcd]e', '/foo/abend' ], False),
(['/foo/a[bcd]e', '/foo/axe' ], False),
(['/foo/a[b-d]e', '/foo/abe' ], True),
(['/foo/a[b-d]e', '/foo/ace' ], True),
(['/foo/a[b-d]e', '/foo/abend' ], False),
(['/foo/a[b-d]e', '/foo/axe' ], False),
(['/foo/a[^bcd]e', '/foo/abe' ], False),
(['/foo/a[^bcd]e', '/foo/abend' ], False),
(['/foo/a[^bcd]e', '/foo/axe' ], True),
(['/foo/{foo,bar,user,other}/bar/', '/foo/user/bar/' ], True),
(['/foo/{foo,bar,user,other}/bar/', '/foo/bar/bar/' ], True),
(['/foo/{foo,bar,user,other}/bar/', '/foo/wrong/bar/' ], False),
(['/foo/{foo,bar,user,other}/test,ca}se/{aa,sd,nd}/bar/', '/foo/user/test,ca}se/aa/bar/' ], True),
(['/foo/{foo,bar,user,other}/test,ca}se/{aa,sd,nd}/bar/', '/foo/bar/test,ca}se/sd/bar/' ], True),
(['/foo/{foo,bar,user,other}/test,ca}se/{aa,sd,nd}/bar/', '/foo/wrong/user/bar/' ], False),
(['/foo/{foo,bar,user,other}/test,ca}se/{aa,sd,nd}/bar/', '/foo/user/wrong/bar/' ], False),
(['/foo/{foo,bar,user,other}/test,ca}se/{aa,sd,nd}/bar/', '/foo/wrong/aa/bar/' ], False),
]
def _run_test(self, params, expected):
regex, path = params
parsed_regex = re.compile(convert_regexp(regex))
self.assertEqual(bool(parsed_regex.search(path)), expected, 'Incorrectly Parsed regex: %s' %regex)
aare_obj = AARE(regex, True)
self.assertEqual(aare_obj.match(path), expected, 'Incorrectly parsed AARE object: %s' % regex)
if not ('*' in path or '{' in path or '}' in path or '?' in path):
self.assertEqual(aare_obj.match(AARE(path, False)), expected, 'Incorrectly parsed AARE object: AARE(%s)' % regex)
def test_multi_usage(self):
aare_obj = AARE('/foo/*', True)
self.assertTrue(aare_obj.match('/foo/bar'))
self.assertFalse(aare_obj.match('/foo/bar/'))
self.assertTrue(aare_obj.match('/foo/asdf'))
def test_match_against_AARE_1(self):
aare_obj_1 = AARE('@{foo}/[a-d]**', True)
aare_obj_2 = AARE('@{foo}/[a-d]**', True)
self.assertTrue(aare_obj_1.match(aare_obj_2))
self.assertTrue(aare_obj_1.is_equal(aare_obj_2))
def test_match_against_AARE_2(self):
aare_obj_1 = AARE('@{foo}/[a-d]**', True)
aare_obj_2 = AARE('@{foo}/*[a-d]*', True)
self.assertFalse(aare_obj_1.match(aare_obj_2))
self.assertFalse(aare_obj_1.is_equal(aare_obj_2))
def test_match_invalid_1(self):
aare_obj = AARE('@{foo}/[a-d]**', True)
with self.assertRaises(AppArmorBug):
aare_obj.match(set())
class TestAAREMatchFromLog(AATest):
tests = [
# AARE log event match expected?
(['/foo/bar', '/foo/bar' ], True),
(['/foo/*', '/foo/bar' ], True),
(['/**', '/foo/bar' ], True),
(['/foo/*', '/bar/foo' ], False),
(['/foo/*', '/foo/"*' ], True),
(['/foo/bar', '/foo/*' ], False),
(['/foo/?', '/foo/(' ], True),
(['/foo/{bar,baz}', '/foo/bar' ], True),
(['/foo/{bar,baz}', '/foo/bars' ], False),
]
def _run_test(self, params, expected):
regex, log_event = params
aare_obj_1 = AARE(regex, True)
aare_obj_2 = AARE(log_event, True, log_event=True)
self.assertEqual(aare_obj_1.match(aare_obj_2), expected)
class TestAAREIsEqual(AATest):
tests = [
# regex is path? check for expected
(['/foo', True, '/foo' ], True ),
(['@{foo}', True, '@{foo}' ], True ),
(['/**', True, '/foo' ], False),
]
def _run_test(self, params, expected):
regex, is_path, check_for = params
aare_obj_1 = AARE(regex, is_path)
aare_obj_2 = AARE(check_for, is_path)
self.assertEqual(expected, aare_obj_1.is_equal(check_for))
self.assertEqual(expected, aare_obj_1.is_equal(aare_obj_2))
def test_is_equal_invalid_1(self):
aare_obj = AARE('/foo/**', True)
with self.assertRaises(AppArmorBug):
aare_obj.is_equal(42)
class TestAAREIsPath(AATest):
tests = [
# regex is path? match for expected
(['/foo*', True, '/foobar' ], True ),
(['@{PROC}/', True, '/foobar' ], False),
(['foo*', False, 'foobar' ], True ),
]
def _run_test(self, params, expected):
regex, is_path, check_for = params
aare_obj = AARE(regex, is_path)
self.assertEqual(expected, aare_obj.match(check_for))
def test_path_missing_slash(self):
with self.assertRaises(AppArmorException):
AARE('foo*', True)
class TestAARERepr(AATest):
def test_repr(self):
obj = AARE('/foo', True)
self.assertEqual(str(obj), "AARE('/foo')")
class TestAAREDeepcopy(AATest):
tests = [
# regex is path? log event expected (dummy value)
(AARE('/foo', False) , True),
(AARE('/foo', False, True) , True),
(AARE('/foo', True) , True),
(AARE('/foo', True, True) , True),
]
def _run_test(self, params, expected):
dup = deepcopy(params)
self.assertTrue(params.match('/foo'))
self.assertTrue(dup.match('/foo'))
self.assertEqual(params.regex, dup.regex)
self.assertEqual(params.orig_regex, dup.orig_regex)
self.assertEqual(params.orig_regex, dup.orig_regex)
class TestAAREglobPath(AATest):
tests = [
# _run_test() will also run each test with '/' appended
# regex expected AARE.regex
('/foo/bar/baz**', '/foo/bar/**'),
('/foo/bar/**baz', '/foo/bar/**'),
('/foo/bar/fo**baz', '/foo/bar/**'),
('/foo/bar/**foo**', '/foo/bar/**'),
('/foo/bar/**f?o**', '/foo/bar/**'),
('/foo/bar/**fo[a-z]**', '/foo/bar/**'),
('/foo/bar/baz', '/foo/bar/*'),
('/foo/bar/baz*', '/foo/bar/*'),
('/foo/bar/*baz', '/foo/bar/*'),
('/foo/bar/fo*baz', '/foo/bar/*'),
('/foo/bar/*foo*', '/foo/bar/*'),
('/foo/bar/b[a-z]z', '/foo/bar/*'),
('/foo/bar/{bar,baz}', '/foo/bar/*'),
('/foo/bar/{bar,ba/z}', '/foo/bar/{bar,ba/*'), # XXX
('/foo/*/baz', '/foo/*/*'),
('/foo/bar/**', '/foo/**'),
('/foo/bar/*', '/foo/**'),
('/foo/**/*', '/foo/**'),
('/foo/*/**', '/foo/**'),
('/foo/*/*', '/foo/**'),
]
def _run_test(self, params, expected):
# test for files
oldpath = AARE(params, True)
newpath = oldpath.glob_path()
self.assertEqual(expected, newpath.regex)
# test for directories
oldpath = AARE(params + '/', True)
newpath = oldpath.glob_path()
self.assertEqual(expected + '/', newpath.regex)
class TestAAREglobPathWithExt(AATest):
tests = [
# _run_test() will also run each test with '/' appended
# regex expected AARE.regex
# no extension - shouldn't change
('/foo/bar/baz**', '/foo/bar/baz**'),
('/foo/bar/**baz', '/foo/bar/**baz'),
('/foo/bar/fo**baz', '/foo/bar/fo**baz'),
('/foo/bar/**foo**', '/foo/bar/**foo**'),
('/foo/bar/**f?o**', '/foo/bar/**f?o**'),
('/foo/bar/**fo[a-z]**', '/foo/bar/**fo[a-z]**'),
('/foo/bar/baz', '/foo/bar/baz'),
('/foo/bar/baz*', '/foo/bar/baz*'),
('/foo/bar/*baz', '/foo/bar/*baz'),
('/foo/bar/fo*baz', '/foo/bar/fo*baz'),
('/foo/bar/*foo*', '/foo/bar/*foo*'),
('/foo/bar/b[a-z]z', '/foo/bar/b[a-z]z'),
('/foo/bar/{bar,baz}', '/foo/bar/{bar,baz}'),
('/foo/bar/{bar,ba/z}', '/foo/bar/{bar,ba/z}'),
('/foo/*/baz', '/foo/*/baz'),
('/foo/bar/**', '/foo/bar/**'),
('/foo/bar/*', '/foo/bar/*'),
('/foo/**/*', '/foo/**/*'),
('/foo/*/**', '/foo/*/**'),
('/foo/*/*', '/foo/*/*'),
# with extension added
('/foo/bar/baz**.xy', '/foo/bar/**.xy'),
('/foo/bar/**baz.xy', '/foo/bar/**.xy'),
('/foo/bar/fo**baz.xy', '/foo/bar/**.xy'),
('/foo/bar/**foo**.xy', '/foo/bar/**.xy'),
('/foo/bar/**f?o**.xy', '/foo/bar/**.xy'),
('/foo/bar/**fo[a-z]**.xy', '/foo/bar/**.xy'),
('/foo/bar/baz.xy', '/foo/bar/*.xy'),
('/foo/bar/baz*.xy', '/foo/bar/*.xy'),
('/foo/bar/*baz.xy', '/foo/bar/*.xy'),
('/foo/bar/fo*baz.xy', '/foo/bar/*.xy'),
('/foo/bar/*foo*.xy', '/foo/bar/*.xy'),
('/foo/bar/b[a-z]z.xy', '/foo/bar/*.xy'),
('/foo/bar/{bar,baz}.xy', '/foo/bar/*.xy'),
('/foo/bar/{bar,ba/z}.xy', '/foo/bar/{bar,ba/*.xy'), # XXX
('/foo/*/baz.xy', '/foo/*/*.xy'),
('/foo/bar/**.xy', '/foo/**.xy'),
('/foo/bar/*.xy', '/foo/**.xy'),
('/foo/**/*.xy', '/foo/**.xy'),
('/foo/*/**.xy', '/foo/**.xy'),
('/foo/*/*.xy', '/foo/**.xy'),
]
def _run_test(self, params, expected):
# test for files
oldpath = AARE(params, True)
newpath = oldpath.glob_path_withext()
self.assertEqual(expected, newpath.regex)
# test for directories - should be kept unchanged
oldpath = AARE(params + '/', True)
newpath = oldpath.glob_path_withext()
self.assertEqual(params + '/', newpath.regex) # note that we compare to params, not expected here
setup_all_loops(__name__)
if __name__ == '__main__':
unittest.main(verbosity=2)