2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-08-22 01:57:43 +00:00
apparmor/utils/test/test-boolean.py
John Johansen c0fcd1698b utils: add support for priority rule prefix
Add basic support for the priority rules prefix. This patch does not
allow the utils to set or suggest priorities. It allows parsing and
retaining of the priority prefix if it already exists on rules and
checking if it's in the supported range.

Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-05-05 14:54:22 -03:00

342 lines
12 KiB
Python

#!/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
from apparmor.common import AppArmorBug, AppArmorException
from apparmor.rule.boolean import BooleanRule, BooleanRuleset
from apparmor.translations import init_translation
from common_test import AATest, setup_all_loops
_ = init_translation()
exp = namedtuple('exp', ('comment', 'varname', 'value'))
# --- tests for single BooleanRule --- #
class BooleanTest(AATest):
def _compare_obj(self, obj, expected):
# boolean variables don't support the allow, audit, deny, or priority keyword
self.assertEqual(False, obj.allow_keyword)
self.assertEqual(False, obj.audit)
self.assertEqual(False, obj.deny)
self.assertEqual(None, obj.priority)
self.assertEqual(expected.varname, obj.varname)
self.assertEqual(expected.value, obj.value)
self.assertEqual(expected.comment, obj.comment)
class BooleanTestParse(BooleanTest):
tests = (
# 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')),
)
def _run_test(self, rawrule, expected):
self.assertTrue(BooleanRule.match(rawrule))
obj = BooleanRule.create_instance(rawrule)
self.assertEqual(rawrule.strip(), obj.raw_rule)
self._compare_obj(obj, expected)
class BooleanTestParseInvalid(BooleanTest):
tests = (
# rawrule matches regex exception
('$foo =', (False, AppArmorException)),
('$ foo = # comment', (False, AppArmorException)),
('${foo = ', (False, AppArmorException)),
# XXX RE_PROFILE_BOOLEAN allows a trailing comma even if the parser disallows it
# ('$foo = true,', (True, AppArmorException)), # trailing comma
# ('$foo = false , ', (True, AppArmorException)), # trailing comma
# ('$foo = true, # comment', (True, AppArmorException)), # trailing comma
)
def _run_test(self, rawrule, expected):
self.assertEqual(BooleanRule.match(rawrule), expected[0])
with self.assertRaises(expected[1]):
BooleanRule.create_instance(rawrule)
class BooleanFromInit(BooleanTest):
# 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)),
# )
def _run_test(self, obj, expected):
self._compare_obj(obj, expected)
class InvalidBooleanInit(AATest):
tests = (
# 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
)
def _run_test(self, params, expected):
with self.assertRaises(expected):
BooleanRule(*params)
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)
def test_invalid_priority_1(self):
with self.assertRaises(AppArmorBug):
BooleanRule('$foo', 'true', priority=-100)
def test_invalid_priority_2(self):
with self.assertRaises(AppArmorBug):
BooleanRule('$foo', 'true', priority=0)
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):
obj = BooleanRule.create_instance(rawrule)
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):
tests = (
# raw rule clean rule
(' $foo = true ', '$foo = true'),
(' $foo = true # comment', '$foo = true'),
(' $foo = false ', '$foo = false'),
(' $foo = false # comment', '$foo = false'),
)
def _run_test(self, rawrule, expected):
self.assertTrue(BooleanRule.match(rawrule))
obj = BooleanRule.create_instance(rawrule)
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):
obj = BooleanRule.create_instance(self.rule)
check_obj = BooleanRule.create_instance(param)
self.assertTrue(BooleanRule.match(param))
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]))
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]))
class BooleanCoveredTest_01(BooleanCoveredTest):
rule = '$foo = true'
tests = (
# 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
)
class BooleanCoveredTest_02(BooleanCoveredTest):
rule = '$foo = false'
tests = (
# 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
)
class BooleanCoveredTest_Invalid(AATest):
def test_borked_obj_is_covered_2(self):
obj = BooleanRule.create_instance('$foo = true')
testobj = BooleanRule('$foo', 'true')
testobj.value = ''
with self.assertRaises(AppArmorBug):
obj.is_covered(testobj)
def test_invalid_is_covered_3(self):
raw_rule = '$foo = true'
class SomeOtherClass(BooleanRule):
pass
obj = BooleanRule.create_instance(raw_rule)
testobj = SomeOtherClass.create_instance(raw_rule) # different type
with self.assertRaises(AppArmorBug):
obj.is_covered(testobj)
def test_invalid_is_equal(self):
raw_rule = '$foo = true'
class SomeOtherClass(BooleanRule):
pass
obj = BooleanRule.create_instance(raw_rule)
testobj = SomeOtherClass.create_instance(raw_rule) # different type
with self.assertRaises(AppArmorBug):
obj.is_equal(testobj)
class BooleanLogprofHeaderTest(AATest):
tests = (
('$foo = true', [_('Boolean Variable'), '$foo = true']),
)
def _run_test(self, params, expected):
obj = BooleanRule.create_instance(params)
self.assertEqual(obj.logprof_header(), expected)
# --- 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:
ruleset.add(BooleanRule.create_instance(rule))
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()
ruleset.add(BooleanRule.create_instance('$foo = true'))
with self.assertRaises(AppArmorException):
ruleset.add(BooleanRule.create_instance('$foo = false')) # attempt to redefine @{foo}
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')
class BooleanDeleteTestAATest(AATest):
pass
setup_all_loops(__name__)
if __name__ == '__main__':
unittest.main(verbosity=1)