PolicyDifference: implement typebounds diff.

Closes #67
This commit is contained in:
Chris PeBenito 2016-02-10 11:44:41 -05:00
parent 699ce33cd7
commit a4d4920d3c
9 changed files with 259 additions and 4 deletions

25
sediff
View File

@ -91,6 +91,7 @@ other.add_argument("--default", action="store_true", help="Print default_* diffe
other.add_argument("--property", action="store_true",
help="Print policy property differences (handle_unknown, version, MLS)")
other.add_argument("--polcap", action="store_true", help="Print policy capability differences")
other.add_argument("--typebounds", action="store_true", help="Print typebounds differences")
args = parser.parse_args()
@ -101,7 +102,7 @@ all_differences = not any((args.class_, args.common, args.type_, args.attribute,
args.role_trans, args.range_trans, args.initialsid, args.genfscon,
args.netifcon, args.nodecon, args.portcon, args.fs_use, args.polcap,
args.property, args.default, args.constrain, args.mlsconstrain,
args.validatetrans, args.mlsvalidatetrans))
args.validatetrans, args.mlsvalidatetrans, args.typebounds))
if args.debug:
logging.basicConfig(level=logging.DEBUG,
@ -1087,6 +1088,28 @@ try:
print()
if all_differences or args.typebounds:
if diff.added_typebounds or diff.removed_typebounds or args.typebounds:
print("Typebounds ({0} Added, {1} Removed, {2} Modified)".format(
len(diff.added_typebounds), len(diff.removed_typebounds),
len(diff.modified_typebounds)))
if diff.added_typebounds and not args.stats:
print(" Added Typebounds: {0}".format(len(diff.added_typebounds)))
for d in sorted(diff.added_typebounds):
print(" + {0}".format(d))
if diff.removed_typebounds and not args.stats:
print(" Removed Typebounds: {0}".format(len(diff.removed_typebounds)))
for d in sorted(diff.removed_typebounds):
print(" - {0}".format(d))
if diff.modified_typebounds and not args.stats:
print(" Modified Typebounds: {0}".format(len(diff.modified_typebounds)))
for bound, added_bound, removed_bound in sorted(
diff.modified_typebounds, key=lambda x: x.rule):
print(" * {0.ruletype} +{1} -{2} {0.child};".format(
bound, added_bound, removed_bound))
print()
except Exception as err:
if args.debug:
import traceback

View File

@ -17,6 +17,7 @@
# <http://www.gnu.org/licenses/>.
#
from .bool import BooleansDifference
from .bounds import BoundsDifference
from .commons import CommonDifference
from .constraints import ConstraintsDifference
from .default import DefaultsDifference
@ -42,6 +43,7 @@ __all__ = ['PolicyDifference']
class PolicyDifference(BooleansDifference,
BoundsDifference,
CategoriesDifference,
CommonDifference,
ConstraintsDifference,

112
setools/diff/bounds.py Normal file
View File

@ -0,0 +1,112 @@
# Copyright 2016, Tresys Technology, LLC
#
# This file is part of SETools.
#
# SETools is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation, either version 2.1 of
# the License, or (at your option) any later version.
#
# SETools 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with SETools. If not, see
# <http://www.gnu.org/licenses/>.
#
from collections import namedtuple
from .descriptors import DiffResultDescriptor
from .difference import Difference, SymbolWrapper, Wrapper
modified_bounds_record = namedtuple("modified_bound", ["rule", "added_bound", "removed_bound"])
class BoundsDifference(Difference):
"""Determine the difference in *bounds between two policies."""
added_typebounds = DiffResultDescriptor("diff_typebounds")
removed_typebounds = DiffResultDescriptor("diff_typebounds")
modified_typebounds = DiffResultDescriptor("diff_typebounds")
# Lists of rules for each policy
_left_typebounds = None
_right_typebounds = None
def diff_typebounds(self):
"""Generate the difference in typebound rules between the policies."""
self.log.info("Generating typebounds differences from {0.left_policy} to {0.right_policy}".
format(self))
if self._left_typebounds is None or self._right_typebounds is None:
self._create_typebound_lists()
self.added_typebounds, self.removed_typebounds, matched_typebounds = self._set_diff(
(BoundsWrapper(c) for c in self._left_typebounds),
(BoundsWrapper(c) for c in self._right_typebounds),
key=lambda b: str(b.child))
self.modified_typebounds = []
for left_bound, right_bound in matched_typebounds:
if SymbolWrapper(left_bound.parent) != SymbolWrapper(right_bound.parent):
self.modified_typebounds.append(modified_bounds_record(
left_bound, right_bound.parent, left_bound.parent))
#
# Internal functions
#
def _create_typebound_lists(self):
"""Create rule lists for both policies."""
self._left_typebounds = []
for rule in self.left_policy.bounds():
if rule.ruletype == "typebounds":
self._left_typebounds.append(rule)
else:
self.log.error("Unknown rule type: {0} (This is an SETools bug)".
format(rule.ruletype))
self._right_typebounds = []
for rule in self.right_policy.bounds():
if rule.ruletype == "typebounds":
self._right_typebounds.append(rule)
else:
self.log.error("Unknown rule type: {0} (This is an SETools bug)".
format(rule.ruletype))
def _reset_diff(self):
"""Reset diff results on policy changes."""
self.log.debug("Resetting all *bounds differences")
self.added_typebounds = None
self.removed_typebounds = None
# Sets of rules for each policy
self._left_typebounds = None
self._right_typebounds = None
class BoundsWrapper(Wrapper):
"""Wrap *bounds for diff purposes."""
def __init__(self, rule):
self.origin = rule
self.ruletype = rule.ruletype
self.parent = SymbolWrapper(rule.parent)
self.child = SymbolWrapper(rule.child)
self.key = hash(rule)
def __hash__(self):
return self.key
def __lt__(self, other):
return self.key < other.key
def __eq__(self, other):
return self.ruletype == other.ruletype and \
self.child == other.child

View File

@ -72,7 +72,7 @@ class Difference(object):
yield Wrapper(expanded_rule)
@staticmethod
def _set_diff(left, right):
def _set_diff(left, right, key=None):
"""
Standard diff of two sets.
@ -108,8 +108,8 @@ class Difference(object):
# instead of giving wrong results. If there is a better way to,
# ensure the items match up, please let me know how or submit a patch.
matched_items = set()
left_matched_items = sorted((left_items - removed_items))
right_matched_items = sorted((right_items - added_items))
left_matched_items = sorted((left_items - removed_items), key=key)
right_matched_items = sorted((right_items - added_items), key=key)
assert len(left_matched_items) == len(right_matched_items), \
"Matched items assertion failure (this is an SETools bug), {0} != {1}". \
format(len(left_matched_items), len(right_matched_items))

View File

@ -37,6 +37,9 @@ class Bounds(PolicySymbol):
def __str__(self):
return "{0.ruletype} {0.parent} {0.child};".format(self)
def __hash__(self):
return hash("{0.ruletype}|{0.child};".format(self))
ruletype = "typebounds"
@property

View File

@ -1521,6 +1521,40 @@ class PolicyDifferenceTest(ValidateRule, unittest.TestCase):
['l1', 'l2', 'dom', 'h1', 'h2', 'dom', 'and', 't3', set(["mls_exempt"]), '==', 'or'],
mlsvalidatetrans.postfix_expression())
#
# typebounds
#
def test_added_typebounds(self):
"""Diff: added typebounds."""
l = sorted(self.diff.added_typebounds)
self.assertEqual(1, len(l))
bounds = l[0]
self.assertEqual("typebounds", bounds.ruletype)
self.assertEqual("added_parent", bounds.parent)
self.assertEqual("added_child", bounds.child)
def test_removed_typebounds(self):
"""Diff: removed typebounds."""
l = sorted(self.diff.removed_typebounds)
self.assertEqual(1, len(l))
bounds = l[0]
self.assertEqual("typebounds", bounds.ruletype)
self.assertEqual("removed_parent", bounds.parent)
self.assertEqual("removed_child", bounds.child)
def test_modified_typebounds(self):
"""Diff: modified typebounds."""
l = sorted(self.diff.modified_typebounds, key=lambda x: x.rule)
self.assertEqual(1, len(l))
bounds, added_bound, removed_bound = l[0]
self.assertEqual("typebounds", bounds.ruletype)
self.assertEqual("mod_child", bounds.child)
self.assertEqual("mod_parent_added", added_bound)
self.assertEqual("mod_parent_removed", removed_bound)
class PolicyDifferenceTestNoDiff(unittest.TestCase):
@ -1899,6 +1933,18 @@ class PolicyDifferenceTestNoDiff(unittest.TestCase):
"""NoDiff: no removed mlsvalidatetrans."""
self.assertFalse(self.diff.removed_mlsvalidatetrans)
def test_added_typebounds(self):
"""NoDiff: no added typebounds."""
self.assertFalse(self.diff.added_typebounds)
def test_removed_typebounds(self):
"""NoDiff: no removed typebounds."""
self.assertFalse(self.diff.removed_typebounds)
def test_modified_typebounds(self):
"""NoDiff: no modified typebounds."""
self.assertFalse(self.diff.modified_typebounds)
class PolicyDifferenceTestMLStoStandard(unittest.TestCase):
@ -2294,3 +2340,15 @@ class PolicyDifferenceTestMLStoStandard(unittest.TestCase):
self.assertEqual(
sum(1 for m in self.diff.left_policy.constraints() if m.ruletype == "mlsvalidatetrans"),
len(self.diff.removed_mlsvalidatetrans))
def test_added_typebounds(self):
"""MLSvsStandardDiff: no added typebounds."""
self.assertFalse(self.diff.added_typebounds)
def test_removed_typebounds(self):
"""MLSvsStandardDiff: no removed typebounds."""
self.assertFalse(self.diff.removed_typebounds)
def test_modified_typebounds(self):
"""MLSvsStandardDiff: no modified typebounds."""
self.assertFalse(self.diff.modified_typebounds)

View File

@ -635,6 +635,25 @@ role role_tr_new_role;
role_transition role_tr_matched_source role_tr_matched_target:infoflow3 role_tr_old_role;
################################################################################
# matching typebounds
type match_parent;
type match_child;
typebounds match_parent match_child;
# removed typebounds
type removed_parent;
type removed_child;
typebounds removed_parent removed_child;
# added typebounds
type added_parent;
type added_child;
# modified typebounds
type mod_parent_removed;
type mod_parent_added;
type mod_child;
typebounds mod_parent_removed mod_child;
# policycaps
policycap open_perms;

View File

@ -557,6 +557,25 @@ role role_tr_new_role;
role_transition role_tr_matched_source role_tr_matched_target:infoflow3 role_tr_old_role;
################################################################################
# matching typebounds
type match_parent;
type match_child;
typebounds match_parent match_child;
# removed typebounds
type removed_parent;
type removed_child;
typebounds removed_parent removed_child;
# added typebounds
type added_parent;
type added_child;
# modified typebounds
type mod_parent_removed;
type mod_parent_added;
type mod_child;
typebounds mod_parent_removed mod_child;
# policycaps
policycap open_perms;

View File

@ -635,6 +635,25 @@ role role_tr_new_role;
role_transition role_tr_matched_source role_tr_matched_target:infoflow3 role_tr_new_role;
################################################################################
# matching typebounds
type match_parent;
type match_child;
typebounds match_parent match_child;
# removed typebounds
type removed_parent;
type removed_child;
# added typebounds
type added_parent;
type added_child;
typebounds added_parent added_child;
# modified typebounds
type mod_parent_removed;
type mod_parent_added;
type mod_child;
typebounds mod_parent_added mod_child;
# policycaps
policycap open_perms;