[apparmor] [patch 2/3] Add tests for NetworkRule and NetworkRuleset
Christian Boltz
apparmor at cboltz.de
Tue Apr 14 21:39:01 UTC 2015
Hello,
this patch adds utils/test/test-network.py with tests for NetworkRule
and NetworkRuleset.
The tests are hopefully self-explaining, so let me just mention the most
important things:
- I started to play with namedtuple, which looks very useful (see "exp")
- the test loops make the tests much more readable (compare with
test-capability.py!) and make it easy to add some more tests
- 100% coverage :-)
[ 45-add-tests-for-NetworkRule.diff ]
=== added file 'utils/test/test-network.py'
--- utils/test/test-network.py 1970-01-01 00:00:00 +0000
+++ utils/test/test-network.py 2015-04-14 21:19:41 +0000
@@ -0,0 +1,428 @@
+#!/usr/bin/env python
+# ----------------------------------------------------------------------
+# Copyright (C) 2015 Christian Boltz <apparmor at 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 common_test import AATest, setup_all_tests
+from collections import namedtuple
+
+from apparmor.rule.network import NetworkRule, NetworkRuleset
+from apparmor.rule import BaseRule, parse_modifiers
+from apparmor.common import AppArmorException, AppArmorBug
+from apparmor.logparser import ReadLog
+
+import re
+
+exp = namedtuple('exp', ['audit', 'allow_keyword', 'deny', 'comment',
+ 'domain', 'all_domains', 'type_or_protocol', 'all_type_or_protocols'])
+
+# --- tests for single NetworkRule --- #
+
+class NetworkTest(AATest):
+ def _compare_obj(self, obj, expected):
+ self.assertEqual(expected.allow_keyword, obj.allow_keyword)
+ self.assertEqual(expected.audit, obj.audit)
+ self.assertEqual(expected.domain, obj.domain)
+ self.assertEqual(expected.type_or_protocol, obj.type_or_protocol)
+ self.assertEqual(expected.all_domains, obj.all_domains)
+ self.assertEqual(expected.all_type_or_protocols, obj.all_type_or_protocols)
+ self.assertEqual(expected.deny, obj.deny)
+ self.assertEqual(expected.comment, obj.comment)
+
+class NetworkTestParse(NetworkTest):
+ tests = [
+ # rawrule audit allow deny comment domain all? type/proto all?
+ ('network,' , exp(False, False, False, '' , None , True , None , True )),
+ ('network inet,' , exp(False, False, False, '' , 'inet', False, None , True )),
+ ('network inet stream,' , exp(False, False, False, '' , 'inet', False, 'stream' , False)),
+ ('deny network inet stream, # comment' , exp(False, False, True , ' # comment' , 'inet', False, 'stream' , False)),
+ ('audit allow network tcp,' , exp(True , True , False, '' , None , True , 'tcp' , False)),
+ ]
+
+ def _run_test(self, rawrule, expected):
+ obj = NetworkRule.parse(rawrule)
+ self.assertEqual(rawrule.strip(), obj.raw_rule)
+ self._compare_obj(obj, expected)
+
+class NetworkTestParseInvalid(NetworkTest):
+ tests = [
+ ('network stream,' , AppArmorException), # domain missing
+ ('network foo,' , AppArmorException),
+ ('network foo bar,' , AppArmorException),
+ ('network foo tcp,' , AppArmorException),
+ ('network inet bar,' , AppArmorException),
+ ]
+
+ def _run_test(self, rawrule, expected):
+ with self.assertRaises(expected):
+ NetworkRule.parse(rawrule)
+
+class NetworkTestParseFromLog(NetworkTest):
+ def test_net_from_log(self):
+ parser = ReadLog('', '', '', '', '')
+ event = 'type=AVC msg=audit(1428699242.551:386): apparmor="DENIED" operation="create" profile="/bin/ping" pid=10589 comm="ping" family="inet" sock_type="raw" protocol=1'
+
+ parsed_event = parser.parse_event(event)
+
+ self.assertEqual(parsed_event, {
+ 'request_mask': set(),
+ 'denied_mask': set(),
+ 'error_code': 0,
+ 'family': 'inet',
+ 'magic_token': 0,
+ 'parent': 0,
+ 'profile': '/bin/ping',
+ 'protocol': 'icmp',
+ 'sock_type': 'raw',
+ 'operation': 'create',
+ 'resource': None,
+ 'info': None,
+ 'aamode': 'REJECTING',
+ 'time': 1428699242,
+ 'active_hat': None,
+ 'pid': 10589,
+ 'task': 0,
+ 'attr': None,
+ 'name2': None,
+ 'name': None,
+ })
+
+ obj = NetworkRule(parsed_event['family'], parsed_event['sock_type'], log_event=parsed_event)
+
+ # audit allow deny comment domain all? type/proto all?
+ expected = exp(False, False, False, '' , 'inet', False, 'raw' , False)
+
+ self._compare_obj(obj, expected)
+
+ self.assertEqual(obj.get_raw(1), ' network inet raw,')
+
+
+class NetworkFromInit(NetworkTest):
+ tests = [
+ # NetworkRule object audit allow deny comment domain all? type/proto all?
+ (NetworkRule('inet', 'raw', deny=True) , exp(False, False, True , '' , 'inet', False, 'raw' , False)),
+ (NetworkRule('inet', 'raw') , exp(False, False, False, '' , 'inet', False, 'raw' , False)),
+ (NetworkRule('inet', NetworkRule.ALL) , exp(False, False, False, '' , 'inet', False, None , True )),
+ (NetworkRule(NetworkRule.ALL, NetworkRule.ALL) , exp(False, False, False, '' , None , True , None , True )),
+ (NetworkRule(NetworkRule.ALL, 'tcp') , exp(False, False, False, '' , None , True , 'tcp' , False)),
+ ]
+
+ def _run_test(self, obj, expected):
+ self._compare_obj(obj, expected)
+
+
+class InvalidNetworkInit(AATest):
+ tests = [
+ # init params expected exception
+ (['inet', '' ] , AppArmorBug), # empty type_or_protocol
+ (['' , 'tcp' ] , AppArmorBug), # empty domain
+ ([' ', 'tcp' ] , AppArmorBug), # whitespace domain
+ (['inet', ' ' ] , AppArmorBug), # whitespace type_or_protocol
+ (['xyxy', 'tcp' ] , AppArmorBug), # invalid domain
+ (['inet', 'xyxy' ] , AppArmorBug), # invalid type_or_protocol
+ ([dict(), 'tcp' ] , AppArmorBug), # wrong type for domain
+ (['inet', dict() ] , AppArmorBug), # wrong type for type_or_protocol
+ ([NetworkRule.ALL, 'stream'] , AppArmorException), # stream requires a domain
+ ]
+
+ def _run_test(self, params, expected):
+ with self.assertRaises(expected):
+ NetworkRule(params[0], params[1])
+
+ def test_missing_params_1(self):
+ with self.assertRaises(TypeError):
+ NetworkRule()
+
+ def test_missing_params_2(self):
+ with self.assertRaises(TypeError):
+ NetworkRule('inet')
+
+
+class InvalidNetworkTest(AATest):
+ def _check_invalid_rawrule(self, rawrule):
+ obj = None
+ with self.assertRaises(AppArmorException):
+ obj = NetworkRule(NetworkRule.parse(rawrule))
+
+ self.assertIsNone(obj, 'NetworkRule handed back an object unexpectedly')
+
+ def test_invalid_net_missing_comma(self):
+ self._check_invalid_rawrule('network') # missing comma
+
+ def test_invalid_net_non_NetworkRule(self):
+ self._check_invalid_rawrule('dbus,') # not a network rule
+
+ def test_parse_modifiers_invalid(self):
+ regex = re.compile('^\s*(?P<audit>audit\s+)?(?P<allow>allow\s+|deny\s+|invalid\s+)?')
+ matches = regex.search('audit invalid ')
+
+ with self.assertRaises(AppArmorBug):
+ parse_modifiers(matches)
+
+ def test_empty_net_data_1(self):
+ obj = NetworkRule('inet', 'stream')
+ obj.domain = ''
+ # no domain set, and ALL not set
+ with self.assertRaises(AppArmorBug):
+ obj.get_clean(1)
+
+ def test_empty_net_data_2(self):
+ obj = NetworkRule('inet', 'stream')
+ obj.type_or_protocol = ''
+ # no type_or_protocol set, and ALL not set
+ with self.assertRaises(AppArmorBug):
+ obj.get_clean(1)
+
+
+class WriteNetworkTestAATest(AATest):
+ def _run_test(self, rawrule, expected):
+ obj = NetworkRule.parse(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
+ (' network , # foo ' , 'network, # foo'),
+ (' audit network inet,' , 'audit network inet,'),
+ (' deny network inet stream,# foo bar' , 'deny network inet stream, # foo bar'),
+ (' deny network inet ,# foo bar' , 'deny network inet, # foo bar'),
+ (' allow network tcp ,# foo bar' , 'allow network tcp, # foo bar'),
+ ]
+
+ def test_write_manually(self):
+ obj = NetworkRule('inet', 'stream', allow_keyword=True)
+
+ expected = ' allow network inet stream,'
+
+ self.assertEqual(expected, obj.get_clean(2), 'unexpected clean rule')
+ self.assertEqual(expected, obj.get_raw(2), 'unexpected raw rule')
+
+
+class NetworkCoveredTest(AATest):
+ def _run_test(self, param, expected):
+ obj = NetworkRule.parse(self.rule)
+ check_obj = NetworkRule.parse(param)
+
+ self.assertEqual(obj.is_equal(check_obj), expected[0], 'Mismatch in is_equal, expected %s' % expected[0])
+ self.assertEqual(obj.is_equal(check_obj, True), expected[1], 'Mismatch in is_equal/strict, expected %s' % expected[1])
+
+ self.assertEqual(obj.is_covered(check_obj), expected[2], 'Mismatch in is_covered, expected %s' % expected[2])
+ self.assertEqual(obj.is_covered(check_obj, True, True), expected[3], 'Mismatch in is_covered/exact, expected %s' % expected[3])
+
+class NetworkCoveredTest_01(NetworkCoveredTest):
+ rule = 'network inet,'
+
+ tests = [
+ # rule equal strict equal covered covered exact
+ ('network,' , [ False , False , False , False ]),
+ ('network inet,' , [ True , True , True , True ]),
+ ('network inet, # comment' , [ True , False , True , True ]),
+ ('allow network inet,' , [ True , False , True , True ]),
+ ('network inet,' , [ True , False , True , True ]),
+ ('network inet stream,' , [ False , False , True , True ]),
+ ('network inet tcp,' , [ False , False , True , True ]),
+ ('audit network inet,' , [ False , False , False , False ]),
+ ('audit network,' , [ False , False , False , False ]),
+ ('network unix,' , [ False , False , False , False ]),
+ ('network tcp,' , [ False , False , False , False ]),
+ ('audit deny network inet,' , [ False , False , False , False ]),
+ ('deny network inet,' , [ False , False , False , False ]),
+ ]
+
+class NetworkCoveredTest_02(NetworkCoveredTest):
+ rule = 'audit network inet,'
+
+ tests = [
+ # rule equal strict equal covered covered exact
+ ( 'network inet,' , [ False , False , True , False ]),
+ ('audit network inet,' , [ True , True , True , True ]),
+ ( 'network inet stream,' , [ False , False , True , False ]),
+ ('audit network inet stream,' , [ False , False , True , True ]),
+ ( 'network,' , [ False , False , False , False ]),
+ ('audit network,' , [ False , False , False , False ]),
+ ('network unix,' , [ False , False , False , False ]),
+ ]
+
+
+class NetworkCoveredTest_03(NetworkCoveredTest):
+ rule = 'network inet stream,'
+
+ tests = [
+ # rule equal strict equal covered covered exact
+ ( 'network inet stream,' , [ True , True , True , True ]),
+ ('allow network inet stream,' , [ True , False , True , True ]),
+ ( 'network inet,' , [ False , False , False , False ]),
+ ( 'network,' , [ False , False , False , False ]),
+ ( 'network inet tcp,' , [ False , False , False , False ]),
+ ('audit network,' , [ False , False , False , False ]),
+ ('audit network inet stream,' , [ False , False , False , False ]),
+ ( 'network unix,' , [ False , False , False , False ]),
+ ( 'network,' , [ False , False , False , False ]),
+ ]
+
+class NetworkCoveredTest_04(NetworkCoveredTest):
+ rule = 'network,'
+
+ tests = [
+ # rule equal strict equal covered covered exact
+ ( 'network,' , [ True , True , True , True ]),
+ ('allow network,' , [ True , False , True , True ]),
+ ( 'network inet,' , [ False , False , True , True ]),
+ ( 'network inet6 stream,' , [ False , False , True , True ]),
+ ( 'network tcp,' , [ False , False , True , True ]),
+ ( 'network inet raw,' , [ False , False , True , True ]),
+ ('audit network,' , [ False , False , False , False ]),
+ ('deny network,' , [ False , False , False , False ]),
+ ]
+
+class NetworkCoveredTest_05(NetworkCoveredTest):
+ rule = 'deny network inet,'
+
+ tests = [
+ # rule equal strict equal covered covered exact
+ ( 'deny network inet,' , [ True , True , True , True ]),
+ ('audit deny network inet,' , [ False , False , False , False ]),
+ ( 'network inet,' , [ False , False , False , False ]), # XXX should covered be true here?
+ ( 'deny network unix,' , [ False , False , False , False ]),
+ ( 'deny network,' , [ False , False , False , False ]),
+ ]
+
+
+class NetworkCoveredTest_Invalid(AATest):
+ def test_borked_obj_is_covered_1(self):
+ obj = NetworkRule.parse('network inet,')
+
+ testobj = NetworkRule('inet', 'stream')
+ testobj.domain = ''
+
+ with self.assertRaises(AppArmorBug):
+ obj.is_covered(testobj)
+
+ def test_borked_obj_is_covered_2(self):
+ obj = NetworkRule.parse('network inet,')
+
+ testobj = NetworkRule('inet', 'stream')
+ testobj.type_or_protocol = ''
+
+ with self.assertRaises(AppArmorBug):
+ obj.is_covered(testobj)
+
+ def test_invalid_is_covered(self):
+ obj = NetworkRule.parse('network inet,')
+
+ testobj = BaseRule() # different type
+
+ with self.assertRaises(AppArmorBug):
+ obj.is_covered(testobj)
+
+ def test_invalid_is_equal(self):
+ obj = NetworkRule.parse('network inet,')
+
+ testobj = BaseRule() # different type
+
+ with self.assertRaises(AppArmorBug):
+ obj.is_equal(testobj)
+
+## --- tests for NetworkRuleset --- #
+
+class NetworkRulesTest(AATest):
+ def test_empty_ruleset(self):
+ ruleset = NetworkRuleset()
+ ruleset_2 = NetworkRuleset()
+ 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 = NetworkRuleset()
+ rules = [
+ 'network tcp,',
+ 'network inet,',
+ ]
+
+ expected_raw = [
+ 'network tcp,',
+ 'network inet,',
+ '',
+ ]
+
+ expected_clean = [
+ 'network inet,',
+ 'network tcp,',
+ '',
+ ]
+
+ for rule in rules:
+ ruleset.add(NetworkRule.parse(rule))
+
+ self.assertEqual(expected_raw, ruleset.get_raw())
+ self.assertEqual(expected_clean, ruleset.get_clean())
+
+ def test_ruleset_2(self):
+ ruleset = NetworkRuleset()
+ rules = [
+ 'network inet6 raw,',
+ 'allow network inet,',
+ 'deny network udp, # example comment',
+ ]
+
+ expected_raw = [
+ ' network inet6 raw,',
+ ' allow network inet,',
+ ' deny network udp, # example comment',
+ '',
+ ]
+
+ expected_clean = [
+ ' deny network udp, # example comment',
+ '',
+ ' allow network inet,',
+ ' network inet6 raw,',
+ '',
+ ]
+
+ for rule in rules:
+ ruleset.add(NetworkRule.parse(rule))
+
+ self.assertEqual(expected_raw, ruleset.get_raw(1))
+ self.assertEqual(expected_clean, ruleset.get_clean(1))
+
+
+class NetworkGlobTestAATest(AATest):
+ def setUp(self):
+ self.maxDiff = None
+ self.ruleset = NetworkRuleset()
+
+ def test_glob_1(self):
+ self.assertEqual(self.ruleset.get_glob('network inet,'), 'network,')
+
+ # not supported or used yet
+ # def test_glob_2(self):
+ # self.assertEqual(self.ruleset.get_glob('network inet raw,'), 'network inet,')
+
+ def test_glob_ext(self):
+ with self.assertRaises(AppArmorBug):
+ # get_glob_ext is not available for network rules
+ self.ruleset.get_glob_ext('network inet raw,')
+
+class NetworkDeleteTestAATest(AATest):
+ pass
+
+if __name__ == '__main__':
+ setup_all_tests()
+ unittest.main(verbosity=2)
Regards,
Christian Boltz
--
Das ist halt der Unterschied: Unix ist ein Betriebssystem mit Tradition,
die anderen sind einfach von sich aus unlogisch. [Anselm Lingnau]
More information about the AppArmor
mailing list