2020-12-25 17:23:12 +01:00
|
|
|
#!/usr/bin/python3
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
# Copyright (C) 2020 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 as published by the Free Software Foundation.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
|
|
import unittest
|
|
|
|
from collections import namedtuple
|
|
|
|
|
2022-08-07 20:32:07 -04:00
|
|
|
from apparmor.common import AppArmorBug, AppArmorException
|
|
|
|
from apparmor.rule.boolean import BooleanRule, BooleanRuleset
|
2020-12-25 17:23:12 +01:00
|
|
|
from apparmor.translations import init_translation
|
2022-08-07 20:32:07 -04:00
|
|
|
from common_test import AATest, setup_all_loops
|
|
|
|
|
2020-12-25 17:23:12 +01:00
|
|
|
_ = init_translation()
|
|
|
|
|
2022-06-18 14:30:49 -04:00
|
|
|
exp = namedtuple('exp', ('comment', 'varname', 'value'))
|
2020-12-25 17:23:12 +01:00
|
|
|
|
|
|
|
# --- tests for single BooleanRule --- #
|
|
|
|
|
2022-08-07 12:26:24 -04:00
|
|
|
|
2020-12-25 17:23:12 +01:00
|
|
|
class BooleanTest(AATest):
|
|
|
|
def _compare_obj(self, obj, expected):
|
|
|
|
# boolean variables don't support the allow, audit or deny keyword
|
|
|
|
self.assertEqual(False, obj.allow_keyword)
|
|
|
|
self.assertEqual(False, obj.audit)
|
|
|
|
self.assertEqual(False, obj.deny)
|
|
|
|
|
|
|
|
self.assertEqual(expected.varname, obj.varname)
|
|
|
|
self.assertEqual(expected.value, obj.value)
|
|
|
|
self.assertEqual(expected.comment, obj.comment)
|
|
|
|
|
2022-08-07 12:26:24 -04:00
|
|
|
|
2020-12-25 17:23:12 +01:00
|
|
|
class BooleanTestParse(BooleanTest):
|
2022-06-18 14:30:49 -04:00
|
|
|
tests = (
|
2022-08-07 12:26:24 -04:00
|
|
|
# rawrule comment varname value
|
|
|
|
('$foo=true', exp('', '$foo', 'true')),
|
|
|
|
('$foo = false', exp('', '$foo', 'false')),
|
|
|
|
('$foo=TrUe', exp('', '$foo', 'true')),
|
|
|
|
('$foo = FaLsE', exp('', '$foo', 'false')),
|
|
|
|
(' $foo = true ', exp('', '$foo', 'true')),
|
|
|
|
(' $foo = true # comment', exp(' # comment', '$foo', 'true')),
|
2022-06-18 14:30:49 -04:00
|
|
|
)
|
2020-12-25 17:23:12 +01:00
|
|
|
|
|
|
|
def _run_test(self, rawrule, expected):
|
|
|
|
self.assertTrue(BooleanRule.match(rawrule))
|
2022-09-10 19:45:22 -04:00
|
|
|
obj = BooleanRule.create_instance(rawrule)
|
2020-12-25 17:23:12 +01:00
|
|
|
self.assertEqual(rawrule.strip(), obj.raw_rule)
|
|
|
|
self._compare_obj(obj, expected)
|
|
|
|
|
2022-08-07 12:26:24 -04:00
|
|
|
|
2020-12-25 17:23:12 +01:00
|
|
|
class BooleanTestParseInvalid(BooleanTest):
|
2022-06-18 14:30:49 -04:00
|
|
|
tests = (
|
2022-08-07 12:26:24 -04:00
|
|
|
# rawrule matches regex exception
|
|
|
|
('$foo =', (False, AppArmorException)),
|
|
|
|
('$ foo = # comment', (False, AppArmorException)),
|
|
|
|
('${foo = ', (False, AppArmorException)),
|
2020-12-25 17:23:12 +01:00
|
|
|
# XXX RE_PROFILE_BOOLEAN allows a trailing comma even if the parser disallows it
|
2022-08-07 12:26:24 -04:00
|
|
|
# ('$foo = true,', (True, AppArmorException)), # trailing comma
|
|
|
|
# ('$foo = false , ', (True, AppArmorException)), # trailing comma
|
|
|
|
# ('$foo = true, # comment', (True, AppArmorException)), # trailing comma
|
2022-06-18 14:30:49 -04:00
|
|
|
)
|
2020-12-25 17:23:12 +01:00
|
|
|
|
|
|
|
def _run_test(self, rawrule, expected):
|
|
|
|
self.assertEqual(BooleanRule.match(rawrule), expected[0])
|
|
|
|
with self.assertRaises(expected[1]):
|
2022-09-10 19:45:22 -04:00
|
|
|
BooleanRule.create_instance(rawrule)
|
2020-12-25 17:23:12 +01:00
|
|
|
|
2022-08-07 12:26:24 -04:00
|
|
|
|
2020-12-25 17:23:12 +01:00
|
|
|
class BooleanFromInit(BooleanTest):
|
2022-08-07 12:26:24 -04:00
|
|
|
# tests = (
|
|
|
|
# # BooleanRule object comment varname value
|
|
|
|
# (BooleanRule('$foo', True), exp('', '$foo', True)),
|
|
|
|
# (BooleanRule('$foo', False), exp('', '$foo', False)),
|
|
|
|
# (BooleanRule('$foo', True, comment='# cmt'), exp('# cmt', '$foo', True)),
|
|
|
|
# (BooleanRule('$foo', False, comment='# cmt'), exp('# cmt', '$foo', False)),
|
|
|
|
# )
|
2020-12-25 17:23:12 +01:00
|
|
|
|
|
|
|
def _run_test(self, obj, expected):
|
|
|
|
self._compare_obj(obj, expected)
|
|
|
|
|
|
|
|
|
|
|
|
class InvalidBooleanInit(AATest):
|
2022-06-18 14:30:49 -04:00
|
|
|
tests = (
|
2022-08-07 12:26:24 -04:00
|
|
|
# init params expected exception
|
|
|
|
((None, True), AppArmorBug), # varname not a str
|
|
|
|
(('', True), AppArmorException), # empty varname
|
|
|
|
(('foo', True), AppArmorException), # varname not starting with '$'
|
|
|
|
(('foo', True), AppArmorException), # varname not starting with '$'
|
|
|
|
|
|
|
|
(('$foo', None), AppArmorBug), # value not a string
|
|
|
|
(('$foo', ''), AppArmorException), # empty value
|
|
|
|
(('$foo', 'maybe'), AppArmorException), # invalid value
|
2022-06-18 14:30:49 -04:00
|
|
|
)
|
2020-12-25 17:23:12 +01:00
|
|
|
|
|
|
|
def _run_test(self, params, expected):
|
|
|
|
with self.assertRaises(expected):
|
2022-06-18 14:30:49 -04:00
|
|
|
BooleanRule(*params)
|
2020-12-25 17:23:12 +01:00
|
|
|
|
|
|
|
def test_missing_params_1(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
BooleanRule()
|
|
|
|
|
|
|
|
def test_missing_params_2(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
BooleanRule('$foo')
|
|
|
|
|
|
|
|
def test_invalid_audit(self):
|
|
|
|
with self.assertRaises(AppArmorBug):
|
|
|
|
BooleanRule('$foo', 'true', audit=True)
|
|
|
|
|
|
|
|
def test_invalid_deny(self):
|
|
|
|
with self.assertRaises(AppArmorBug):
|
|
|
|
BooleanRule('$foo', 'true', deny=True)
|
|
|
|
|
|
|
|
|
|
|
|
class InvalidBooleanTest(AATest):
|
|
|
|
def _check_invalid_rawrule(self, rawrule, matches_regex=False):
|
|
|
|
obj = None
|
|
|
|
self.assertEqual(BooleanRule.match(rawrule), matches_regex)
|
|
|
|
with self.assertRaises(AppArmorException):
|
2022-09-10 19:45:22 -04:00
|
|
|
obj = BooleanRule.create_instance(rawrule)
|
2020-12-25 17:23:12 +01:00
|
|
|
|
|
|
|
self.assertIsNone(obj, 'BooleanRule handed back an object unexpectedly')
|
|
|
|
|
|
|
|
def test_invalid_missing_value(self):
|
|
|
|
self._check_invalid_rawrule('$foo = ', matches_regex=False) # missing value
|
|
|
|
|
|
|
|
def test_invalid_net_non_BooleanRule(self):
|
|
|
|
self._check_invalid_rawrule('dbus,') # not a boolean rule
|
|
|
|
|
|
|
|
|
|
|
|
class WriteBooleanTestAATest(AATest):
|
2022-06-18 14:30:49 -04:00
|
|
|
tests = (
|
2022-08-07 12:26:24 -04:00
|
|
|
# raw rule clean rule
|
|
|
|
(' $foo = true ', '$foo = true'),
|
|
|
|
(' $foo = true # comment', '$foo = true'),
|
|
|
|
(' $foo = false ', '$foo = false'),
|
|
|
|
(' $foo = false # comment', '$foo = false'),
|
2022-06-18 14:30:49 -04:00
|
|
|
)
|
2020-12-25 17:23:12 +01:00
|
|
|
|
|
|
|
def _run_test(self, rawrule, expected):
|
|
|
|
self.assertTrue(BooleanRule.match(rawrule))
|
2022-09-10 19:45:22 -04:00
|
|
|
obj = BooleanRule.create_instance(rawrule)
|
2020-12-25 17:23:12 +01:00
|
|
|
clean = obj.get_clean()
|
|
|
|
raw = obj.get_raw()
|
|
|
|
|
|
|
|
self.assertEqual(expected.strip(), clean, 'unexpected clean rule')
|
|
|
|
self.assertEqual(rawrule.strip(), raw, 'unexpected raw rule')
|
|
|
|
|
|
|
|
def test_write_manually_1(self):
|
|
|
|
obj = BooleanRule('$foo', 'true')
|
|
|
|
|
|
|
|
expected = ' $foo = true'
|
|
|
|
|
|
|
|
self.assertEqual(expected, obj.get_clean(2), 'unexpected clean rule')
|
|
|
|
self.assertEqual(expected, obj.get_raw(2), 'unexpected raw rule')
|
|
|
|
|
|
|
|
def test_write_manually_2(self):
|
|
|
|
obj = BooleanRule('$foo', 'false')
|
|
|
|
|
|
|
|
expected = ' $foo = false'
|
|
|
|
|
|
|
|
self.assertEqual(expected, obj.get_clean(2), 'unexpected clean rule')
|
|
|
|
self.assertEqual(expected, obj.get_raw(2), 'unexpected raw rule')
|
|
|
|
|
|
|
|
|
|
|
|
class BooleanCoveredTest(AATest):
|
|
|
|
def _run_test(self, param, expected):
|
2022-09-10 19:45:22 -04:00
|
|
|
obj = BooleanRule.create_instance(self.rule)
|
|
|
|
check_obj = BooleanRule.create_instance(param)
|
2020-12-25 17:23:12 +01:00
|
|
|
|
|
|
|
self.assertTrue(BooleanRule.match(param))
|
|
|
|
|
2023-02-19 16:26:14 -05:00
|
|
|
self.assertEqual(obj.is_equal(check_obj), expected[0], 'Mismatch in is_equal, expected {}'.format(expected[0]))
|
|
|
|
self.assertEqual(obj.is_equal(check_obj, True), expected[1], 'Mismatch in is_equal/strict, expected {}'.format(expected[1]))
|
2020-12-25 17:23:12 +01:00
|
|
|
|
2023-02-19 16:26:14 -05:00
|
|
|
self.assertEqual(obj.is_covered(check_obj), expected[2], 'Mismatch in is_covered, expected {}'.format(expected[2]))
|
|
|
|
self.assertEqual(obj.is_covered(check_obj, True, True), expected[3], 'Mismatch in is_covered/exact, expected {}'.format(expected[3]))
|
2020-12-25 17:23:12 +01:00
|
|
|
|
2022-08-07 12:26:24 -04:00
|
|
|
|
2020-12-25 17:23:12 +01:00
|
|
|
class BooleanCoveredTest_01(BooleanCoveredTest):
|
|
|
|
rule = '$foo = true'
|
|
|
|
|
2022-06-18 14:30:49 -04:00
|
|
|
tests = (
|
2022-08-07 12:26:24 -04:00
|
|
|
# rule equal strict equal covered covered exact
|
|
|
|
(' $foo = true', (True, True, True, True)),
|
|
|
|
(' $foo = TRUE', (True, False, True, True)), # upper vs. lower case
|
|
|
|
(' $foo = true # comment', (True, False, True, True)),
|
|
|
|
(' $foo = false', (False, False, False, False)),
|
|
|
|
(' $foo = false # cmt', (False, False, False, False)),
|
|
|
|
(' $bar = true', (False, False, False, False)), # different variable name
|
2022-06-18 14:30:49 -04:00
|
|
|
)
|
2020-12-25 17:23:12 +01:00
|
|
|
|
2022-08-07 12:26:24 -04:00
|
|
|
|
2020-12-25 17:23:12 +01:00
|
|
|
class BooleanCoveredTest_02(BooleanCoveredTest):
|
|
|
|
rule = '$foo = false'
|
|
|
|
|
2022-06-18 14:30:49 -04:00
|
|
|
tests = (
|
2022-08-07 12:26:24 -04:00
|
|
|
# rule equal strict equal covered covered exact
|
|
|
|
(' $foo = false', (True, True, True, True)),
|
|
|
|
(' $foo = false # comment', (True, False, True, True)),
|
|
|
|
(' $foo = true', (False, False, False, False)),
|
|
|
|
(' $foo = true # cmt', (False, False, False, False)),
|
|
|
|
(' $bar = false', (False, False, False, False)), # different variable name
|
2022-06-18 14:30:49 -04:00
|
|
|
)
|
2020-12-25 17:23:12 +01:00
|
|
|
|
2022-08-07 12:26:24 -04:00
|
|
|
|
2020-12-25 17:23:12 +01:00
|
|
|
class BooleanCoveredTest_Invalid(AATest):
|
|
|
|
def test_borked_obj_is_covered_2(self):
|
2022-09-10 19:45:22 -04:00
|
|
|
obj = BooleanRule.create_instance('$foo = true')
|
2020-12-25 17:23:12 +01:00
|
|
|
|
|
|
|
testobj = BooleanRule('$foo', 'true')
|
|
|
|
testobj.value = ''
|
|
|
|
|
|
|
|
with self.assertRaises(AppArmorBug):
|
|
|
|
obj.is_covered(testobj)
|
|
|
|
|
|
|
|
def test_invalid_is_covered_3(self):
|
2022-09-27 22:14:31 -04:00
|
|
|
raw_rule = '$foo = true'
|
|
|
|
class SomeOtherClass(BooleanRule):
|
|
|
|
pass
|
2020-12-25 17:23:12 +01:00
|
|
|
|
2022-09-27 22:14:31 -04:00
|
|
|
obj = BooleanRule.create_instance(raw_rule)
|
|
|
|
testobj = SomeOtherClass.create_instance(raw_rule) # different type
|
2020-12-25 17:23:12 +01:00
|
|
|
with self.assertRaises(AppArmorBug):
|
|
|
|
obj.is_covered(testobj)
|
|
|
|
|
|
|
|
def test_invalid_is_equal(self):
|
2022-09-27 22:14:31 -04:00
|
|
|
raw_rule = '$foo = true'
|
|
|
|
class SomeOtherClass(BooleanRule):
|
|
|
|
pass
|
2020-12-25 17:23:12 +01:00
|
|
|
|
2022-09-27 22:14:31 -04:00
|
|
|
obj = BooleanRule.create_instance(raw_rule)
|
|
|
|
testobj = SomeOtherClass.create_instance(raw_rule) # different type
|
2020-12-25 17:23:12 +01:00
|
|
|
with self.assertRaises(AppArmorBug):
|
|
|
|
obj.is_equal(testobj)
|
|
|
|
|
2022-08-07 12:26:24 -04:00
|
|
|
|
2020-12-25 17:23:12 +01:00
|
|
|
class BooleanLogprofHeaderTest(AATest):
|
2022-06-18 14:30:49 -04:00
|
|
|
tests = (
|
2022-08-07 12:26:24 -04:00
|
|
|
('$foo = true', [_('Boolean Variable'), '$foo = true']),
|
2022-06-18 14:30:49 -04:00
|
|
|
)
|
2020-12-25 17:23:12 +01:00
|
|
|
|
|
|
|
def _run_test(self, params, expected):
|
2022-09-10 19:45:22 -04:00
|
|
|
obj = BooleanRule.create_instance(params)
|
2020-12-25 17:23:12 +01:00
|
|
|
self.assertEqual(obj.logprof_header(), expected)
|
|
|
|
|
2022-08-07 12:26:24 -04:00
|
|
|
|
2020-12-25 17:23:12 +01:00
|
|
|
# --- tests for BooleanRuleset --- #
|
|
|
|
|
|
|
|
class BooleanRulesTest(AATest):
|
|
|
|
def test_empty_ruleset(self):
|
|
|
|
ruleset = BooleanRuleset()
|
|
|
|
ruleset_2 = BooleanRuleset()
|
|
|
|
self.assertEqual([], ruleset.get_raw(2))
|
|
|
|
self.assertEqual([], ruleset.get_clean(2))
|
|
|
|
self.assertEqual([], ruleset_2.get_raw(2))
|
|
|
|
self.assertEqual([], ruleset_2.get_clean(2))
|
|
|
|
|
|
|
|
def test_ruleset_1(self):
|
|
|
|
ruleset = BooleanRuleset()
|
|
|
|
rules = [
|
|
|
|
'$foo = true',
|
|
|
|
'$baz= false',
|
|
|
|
]
|
|
|
|
|
|
|
|
expected_raw = [
|
|
|
|
'$foo = true',
|
|
|
|
'$baz= false',
|
|
|
|
'',
|
|
|
|
]
|
|
|
|
|
|
|
|
expected_clean = [
|
|
|
|
'$baz = false',
|
|
|
|
'$foo = true',
|
|
|
|
'',
|
|
|
|
]
|
|
|
|
|
|
|
|
expected_clean_unsorted = [
|
|
|
|
'$foo = true',
|
|
|
|
'$baz = false',
|
|
|
|
'',
|
|
|
|
]
|
|
|
|
|
|
|
|
for rule in rules:
|
2022-09-10 19:45:22 -04:00
|
|
|
ruleset.add(BooleanRule.create_instance(rule))
|
2020-12-25 17:23:12 +01:00
|
|
|
|
|
|
|
self.assertEqual(expected_raw, ruleset.get_raw())
|
|
|
|
self.assertEqual(expected_clean, ruleset.get_clean())
|
|
|
|
self.assertEqual(expected_clean_unsorted, ruleset.get_clean_unsorted())
|
|
|
|
|
|
|
|
def test_ruleset_overwrite(self):
|
|
|
|
ruleset = BooleanRuleset()
|
|
|
|
|
2022-09-10 19:45:22 -04:00
|
|
|
ruleset.add(BooleanRule.create_instance('$foo = true'))
|
2020-12-25 17:23:12 +01:00
|
|
|
with self.assertRaises(AppArmorException):
|
2022-09-10 19:45:22 -04:00
|
|
|
ruleset.add(BooleanRule.create_instance('$foo = false')) # attempt to redefine @{foo}
|
2020-12-25 17:23:12 +01:00
|
|
|
|
2022-08-07 12:26:24 -04:00
|
|
|
|
2020-12-25 17:23:12 +01:00
|
|
|
class BooleanGlobTestAATest(AATest):
|
|
|
|
def setUp(self):
|
|
|
|
self.ruleset = BooleanRuleset()
|
|
|
|
|
|
|
|
# def test_glob_1(self):
|
|
|
|
# with self.assertRaises(NotImplementedError):
|
|
|
|
# self.ruleset.get_glob('$foo = true')
|
|
|
|
|
|
|
|
def test_glob_ext(self):
|
|
|
|
with self.assertRaises(NotImplementedError):
|
|
|
|
# get_glob_ext is not available for boolean rules
|
|
|
|
self.ruleset.get_glob_ext('$foo = true')
|
|
|
|
|
2022-08-07 12:26:24 -04:00
|
|
|
|
2020-12-25 17:23:12 +01:00
|
|
|
class BooleanDeleteTestAATest(AATest):
|
|
|
|
pass
|
|
|
|
|
2022-08-07 12:26:24 -04:00
|
|
|
|
2020-12-25 17:23:12 +01:00
|
|
|
setup_all_loops(__name__)
|
|
|
|
if __name__ == '__main__':
|
|
|
|
unittest.main(verbosity=1)
|