apparmor/utils/test/test-abi.py
2023-02-19 16:54:38 -05:00

360 lines
13 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 common_test import AATest, setup_all_loops
from apparmor.common import AppArmorBug, AppArmorException
from apparmor.rule.abi import AbiRule, AbiRuleset
from apparmor.translations import init_translation
_ = init_translation()
exp = namedtuple(
'exp', ( # 'audit', 'allow_keyword', 'deny',
'comment', 'path', 'ifexists', 'ismagic'))
# --- tests for single AbiRule --- #
class AbiTest(AATest):
def _compare_obj(self, obj, expected):
self.assertEqual(False, obj.allow_keyword) # not supported in abi rules, expected to be always False
self.assertEqual(False, obj.audit) # not supported in abi rules, expected to be always False
self.assertEqual(False, obj.deny) # not supported in abi rules, expected to be always False
self.assertEqual(expected.comment, obj.comment)
self.assertEqual(expected.path, obj.path)
self.assertEqual(False, expected.ifexists) # tests bug - should always expect ifexists==False
self.assertEqual(False, obj.ifexists) # not supported in abi rules, expected to be always False
self.assertEqual(expected.ismagic, obj.ismagic)
class AbiTestParse(AbiTest):
tests = (
# AbiRule object comment path if exists ismagic
('abi <abstractions/base>,', exp('', 'abstractions/base', False, True)), # magic path
('abi <abstractions/base>, # comment', exp(' # comment', 'abstractions/base', False, True)),
('abi<abstractions/base>,#comment', exp(' #comment', 'abstractions/base', False, True)),
(' abi <abstractions/base> , ', exp('', 'abstractions/base', False, True)),
('abi "/foo/bar",', exp('', '/foo/bar', False, False)), # absolute path
('abi "/foo/bar", # comment', exp(' # comment', '/foo/bar', False, False)),
('abi "/foo/bar",#comment', exp(' #comment', '/foo/bar', False, False)),
(' abi "/foo/bar" , ', exp('', '/foo/bar', False, False)),
)
def _run_test(self, rawrule, expected):
self.assertTrue(AbiRule.match(rawrule))
obj = AbiRule.create_instance(rawrule)
self.assertEqual(rawrule.strip(), obj.raw_rule)
self._compare_obj(obj, expected)
class AbiTestParseInvalid(AbiTest):
# tests = (
# (' some abi <abstractions/base>', AppArmorException),
# (' /etc/fstab r,', AppArmorException),
# ('/usr/abi r,', AppArmorException),
# ('/abi r,', AppArmorException),
# )
def _run_test(self, rawrule, expected):
self.assertTrue(AbiRule.match(rawrule)) # the above invalid rules still match the main regex!
with self.assertRaises(expected):
AbiRule.create_instance(rawrule)
# class AbiTestParseFromLog(AbiTest): # we'll never have log events for abi
class AbiFromInit(AbiTest):
tests = (
# AbiRule object ifexists ismagic comment path ifexists ismagic
(AbiRule('abi/4.19', False, False), exp('', 'abi/4.19', False, False)),
(AbiRule('foo', False, False), exp('', 'foo', False, False)),
(AbiRule('bar', False, True), exp('', 'bar', False, True)),
(AbiRule('comment', False, False, comment='# cmt'), exp('# cmt', 'comment', False, False)),
)
def _run_test(self, obj, expected):
self._compare_obj(obj, expected)
class InvalidAbiInit(AATest):
tests = (
# init params expected exception
((False, False, False), AppArmorBug), # wrong type for path
(('', False, False), AppArmorBug), # empty path
((None, False, False), AppArmorBug), # wrong type for path
# ((' ', False, False), AppArmorBug), # whitespace-only path
(('foo', None, False), AppArmorBug), # wrong type for ifexists
(('foo', '', False), AppArmorBug), # wrong type for ifexists
(('foo', False, None), AppArmorBug), # wrong type for ismagic
(('foo', False, ''), AppArmorBug), # wrong type for ismagic
(('', True, False), AppArmorBug), # ifexists set
)
def _run_test(self, params, expected):
with self.assertRaises(expected):
AbiRule(*params)
def test_missing_params_1(self):
with self.assertRaises(TypeError):
AbiRule()
def test_missing_params_2(self):
with self.assertRaises(TypeError):
AbiRule('foo')
def test_missing_params_3(self):
with self.assertRaises(TypeError):
AbiRule('foo', False)
def test_audit_true(self):
with self.assertRaises(AppArmorBug):
AbiRule('foo', False, False, audit=True)
def test_deny_true(self):
with self.assertRaises(AppArmorBug):
AbiRule('foo', False, False, deny=True)
def test_ifexists_true(self):
with self.assertRaises(AppArmorBug):
AbiRule('foo', True, False)
class InvalidAbiTest(AATest):
def _check_invalid_rawrule(self, rawrule, matches_regex=False):
obj = None
self.assertEqual(AbiRule.match(rawrule), matches_regex)
with self.assertRaises(AppArmorException):
obj = AbiRule.create_instance(rawrule)
self.assertIsNone(obj, 'AbiRule handed back an object unexpectedly')
def test_invalid_abi_missing_path(self):
self._check_invalid_rawrule('abi ,', matches_regex=True) # missing path
def test_invalid_non_AbiRule(self):
self._check_invalid_rawrule('dbus,') # not a abi rule
# def test_empty_data_1(self):
# obj = AbiRule('foo', False, False)
# obj.path = ''
# # no path set
# with self.assertRaises(AppArmorBug):
# obj.get_clean(1)
class WriteAbiTestAATest(AATest):
def _run_test(self, rawrule, expected):
self.assertTrue(AbiRule.match(rawrule))
obj = AbiRule.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')
tests = (
# raw rule clean rule
(' abi <foo> , ', 'abi <foo>,'),
(' abi foo , ', 'abi "foo",'),
(' abi "foo" , ', 'abi "foo",'),
(' abi /foo , ', 'abi "/foo",'),
(' abi "/foo" , ', 'abi "/foo",'),
(' abi <foo>, # bar ', 'abi <foo>, # bar'),
(' abi foo , # bar ', 'abi "foo", # bar'),
(' abi "foo", # bar ', 'abi "foo", # bar'),
(' abi /foo, # bar ', 'abi "/foo", # bar'),
(' abi "/foo", # bar ', 'abi "/foo", # bar'),
)
def test_write_manually(self):
obj = AbiRule('abs/foo', False, True, comment=' # cmt')
expected = ' abi <abs/foo>, # cmt'
self.assertEqual(expected, obj.get_clean(2), 'unexpected clean rule')
self.assertEqual(expected, obj.get_raw(2), 'unexpected raw rule')
class AbiCoveredTest(AATest):
def _run_test(self, param, expected):
obj = AbiRule.create_instance(self.rule)
check_obj = AbiRule.create_instance(param)
self.assertTrue(AbiRule.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 AbiCoveredTest_01(AbiCoveredTest):
rule = 'abi <foo>,'
tests = (
# rule equal strict equal covered covered exact
('abi <foo>,', (True, True, True, True)),
('abi "foo",', (False, False, False, False)),
('abi <foobar>,', (False, False, False, False)),
('abi "foo",', (False, False, False, False)),
)
class AbiCoveredTest_02(AbiCoveredTest):
rule = 'abi "foo",'
tests = (
# rule equal strict equal covered covered exact
('abi <foo>,', (False, False, False, False)),
('abi "foo",', (True, True, True, True)),
('abi "foobar",', (False, False, False, False)),
('abi foo,', (True, False, True, True)),
)
# class AbiCoveredTest_Invalid(AATest):
# def test_borked_obj_is_covered_1(self):
# obj = AbiRule.create_instance('abi <foo>')
#
# testobj = AbiRule('foo', True, True)
# testobj.path = ''
#
# with self.assertRaises(AppArmorBug):
# obj.is_covered(testobj)
#
# def test_borked_obj_is_covered_2(self):
# obj = AbiRule.create_instance('abi send set=quit peer=/foo,')
#
# testobj = AbiRule('send', 'quit', '/foo')
# testobj.abi = ''
#
# with self.assertRaises(AppArmorBug):
# obj.is_covered(testobj)
#
# def test_borked_obj_is_covered_3(self):
# obj = AbiRule.create_instance('abi send set=quit peer=/foo,')
#
# testobj = AbiRule('send', 'quit', '/foo')
# testobj.peer = ''
#
# with self.assertRaises(AppArmorBug):
# obj.is_covered(testobj)
#
# def test_invalid_is_covered(self):
# raw_rule = 'abi send,'
# class SomeOtherClass(AbiRule):
# pass
#
# obj = AbiRule.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 = 'abi send,'
# class SomeOtherClass(AbiRule):
# pass
#
# obj = AbiRule.create_instance(raw_rule)
# testobj = SomeOtherClass.create_instance(raw_rule) # different type
# with self.assertRaises(AppArmorBug):
# obj.is_equal(testobj)
class AbiLogprofHeaderTest(AATest):
tests = (
('abi <abi/3.0>,', [_('Abi'), 'abi <abi/3.0>,']),
('abi "/foo/bar",', [_('Abi'), 'abi "/foo/bar",']),
)
def _run_test(self, params, expected):
obj = AbiRule.create_instance(params)
self.assertEqual(obj.logprof_header(), expected)
## --- tests for AbiRuleset --- #
class AbiRulesTest(AATest):
def test_empty_ruleset(self):
ruleset = AbiRuleset()
ruleset_2 = AbiRuleset()
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))
self.assertEqual([], ruleset_2.get_clean_unsorted(2))
def test_ruleset_1(self):
ruleset = AbiRuleset()
rules = (
' abi <foo> ,',
' abi "/bar", ',
)
expected_raw = [
'abi <foo> ,',
'abi "/bar",',
'',
]
expected_clean = [
'abi "/bar",',
'abi <foo>,',
'',
]
expected_clean_unsorted = [
'abi <foo>,',
'abi "/bar",',
'',
]
for rule in rules:
ruleset.add(AbiRule.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())
class AbiGlobTestAATest(AATest):
def setUp(self):
self.maxDiff = None
self.ruleset = AbiRuleset()
# def test_glob(self):
# with self.assertRaises(NotImplementedError):
# # get_glob_ext is not available for include rules
# self.ruleset.get_glob('include send set=int,')
def test_glob_ext(self):
with self.assertRaises(NotImplementedError):
# get_glob_ext is not available for include rules
self.ruleset.get_glob_ext('include send set=int,')
# class AbiDeleteTestAATest(AATest):
# pass
setup_all_loops(__name__)
if __name__ == '__main__':
unittest.main(verbosity=1)