mirror of
https://github.com/SELinuxProject/setools
synced 2025-03-25 04:26:28 +00:00
Implement infoflow subgraph to handle min weight and excluded types.
Infoflow now will create a complete graph for the policy and then create a subgraph to filter out nodes based on excluded types and edges based on minimum weight. The main graph will only need to be rebuilt if there is a change in the permission map. While this is a little more expensive for seinfoflow, it should make interactive analysis in apol faster since repeatedly deriving a subgraph will be faster than repeatedly rebuilding the entire graph.
This commit is contained in:
parent
5bf48a4a1f
commit
8a07be100f
@ -45,6 +45,7 @@ class InfoFlowAnalysis(object):
|
||||
self.set_perm_map(perm_map)
|
||||
self.set_exclude(exclude)
|
||||
self.rebuildgraph = True
|
||||
self.rebuildsubgraph = True
|
||||
|
||||
self.G = nx.DiGraph()
|
||||
|
||||
@ -63,7 +64,7 @@ class InfoFlowAnalysis(object):
|
||||
"Min information flow weight must be an integer 1-10.")
|
||||
|
||||
self.minweight = w
|
||||
self.rebuildgraph = True
|
||||
self.rebuildsubgraph = True
|
||||
|
||||
def set_perm_map(self, perm_map):
|
||||
"""
|
||||
@ -85,6 +86,7 @@ class InfoFlowAnalysis(object):
|
||||
self.perm_map = perm_map
|
||||
|
||||
self.rebuildgraph = True
|
||||
self.rebuildsubgraph = True
|
||||
|
||||
def set_exclude(self, exclude):
|
||||
"""
|
||||
@ -96,6 +98,8 @@ class InfoFlowAnalysis(object):
|
||||
|
||||
self.exclude = [self.policy.lookup_type(t) for t in exclude]
|
||||
|
||||
self.rebuildsubgraph = True
|
||||
|
||||
def __get_steps(self, path):
|
||||
"""
|
||||
Generator which returns the source, target, and associated rules
|
||||
@ -135,12 +139,12 @@ class InfoFlowAnalysis(object):
|
||||
s = self.policy.lookup_type(source)
|
||||
t = self.policy.lookup_type(target)
|
||||
|
||||
if self.rebuildgraph:
|
||||
self._build_graph()
|
||||
if self.rebuildsubgraph:
|
||||
self._build_subgraph()
|
||||
|
||||
if s in self.G and t in self.G:
|
||||
if s in self.subG and t in self.subG:
|
||||
try:
|
||||
path = nx.shortest_path(self.G, s, t)
|
||||
path = nx.shortest_path(self.subG, s, t)
|
||||
except nx.exception.NetworkXNoPath:
|
||||
pass
|
||||
else:
|
||||
@ -171,12 +175,12 @@ class InfoFlowAnalysis(object):
|
||||
s = self.policy.lookup_type(source)
|
||||
t = self.policy.lookup_type(target)
|
||||
|
||||
if self.rebuildgraph:
|
||||
self._build_graph()
|
||||
if self.rebuildsubgraph:
|
||||
self._build_subgraph()
|
||||
|
||||
if s in self.G and t in self.G:
|
||||
if s in self.subG and t in self.subG:
|
||||
try:
|
||||
paths = nx.all_simple_paths(self.G, s, t, maxlen)
|
||||
paths = nx.all_simple_paths(self.subG, s, t, maxlen)
|
||||
except nx.exception.NetworkXNoPath:
|
||||
pass
|
||||
else:
|
||||
@ -203,12 +207,12 @@ class InfoFlowAnalysis(object):
|
||||
s = self.policy.lookup_type(source)
|
||||
t = self.policy.lookup_type(target)
|
||||
|
||||
if self.rebuildgraph:
|
||||
self._build_graph()
|
||||
if self.rebuildsubgraph:
|
||||
self._build_subgraph()
|
||||
|
||||
if s in self.G and t in self.G:
|
||||
if s in self.subG and t in self.subG:
|
||||
try:
|
||||
paths = nx.all_shortest_paths(self.G, s, t)
|
||||
paths = nx.all_shortest_paths(self.subG, s, t)
|
||||
except nx.exception.NetworkXNoPath:
|
||||
pass
|
||||
else:
|
||||
@ -231,10 +235,10 @@ class InfoFlowAnalysis(object):
|
||||
"""
|
||||
s = self.policy.lookup_type(type_)
|
||||
|
||||
if self.rebuildgraph:
|
||||
self._build_graph()
|
||||
if self.rebuildsubgraph:
|
||||
self._build_subgraph()
|
||||
|
||||
for source, target, data in self.G.out_edges_iter(s, data=True):
|
||||
for source, target, data in self.subG.out_edges_iter(s, data=True):
|
||||
yield source, target, data["rules"]
|
||||
|
||||
def get_stats(self):
|
||||
@ -254,13 +258,18 @@ class InfoFlowAnalysis(object):
|
||||
# (Internal) Graph building functions
|
||||
#
|
||||
#
|
||||
# 1. __build_graph determines the flow in each direction for each TE
|
||||
# rule and then expands the rule (ignoring excluded types)
|
||||
# 2. __add_flow Simply creates edges in the appropriate direction.
|
||||
# (decrease chance of coding errors for graph operations)
|
||||
# 3. __add_edge does the actual graph insertions. Nodes are implictly
|
||||
# 1. _build_graph determines the flow in each direction for each TE
|
||||
# rule and then expands the rule. All information flows are
|
||||
# included in this main graph: memory is traded off for efficiency
|
||||
# as the main graph should only need to be rebuilt if permission
|
||||
# weights change.
|
||||
# 2. __add_edge does the actual graph insertions. Nodes are implictly
|
||||
# created by the edge additions, i.e. types that have no info flow
|
||||
# due to permission weights or are excluded do not appear in the graph.
|
||||
# do not appear in the graph.
|
||||
# 3. _build_subgraph derives a subgraph which removes all excluded
|
||||
# types (nodes) and edges (information flows) which are below the
|
||||
# minimum weight. This subgraph is rebuilt only if the main graph
|
||||
# is rebuilt or the minimum weight or excluded types change.
|
||||
def __add_edge(self, source, target, rule, weight):
|
||||
# use capacity to store the info flow weight so
|
||||
# we can use network flow algorithms naturally.
|
||||
@ -274,18 +283,6 @@ class InfoFlowAnalysis(object):
|
||||
self.G.add_edge(
|
||||
source, target, capacity=weight, weight=1, rules=[rule])
|
||||
|
||||
def __add_flow(self, source, target, rule, ww, rw):
|
||||
assert max(ww, rw) >= self.minweight
|
||||
|
||||
# only add flows if they actually flow
|
||||
# in our out of the source type type
|
||||
if source != target:
|
||||
if ww >= self.minweight:
|
||||
self.__add_edge(source, target, rule, ww)
|
||||
|
||||
if rw >= self.minweight:
|
||||
self.__add_edge(target, source, rule, rw)
|
||||
|
||||
def _build_graph(self):
|
||||
self.G.clear()
|
||||
|
||||
@ -295,12 +292,33 @@ class InfoFlowAnalysis(object):
|
||||
|
||||
(rweight, wweight) = self.perm_map.rule_weight(r)
|
||||
|
||||
# 1. only proceed if weight meets or exceeds the minimum weight
|
||||
# 2. expand source and target to handle attributes
|
||||
# 3. ignore flow if one of the types is in the exclude list
|
||||
if max(rweight, wweight) >= self.minweight:
|
||||
for s, t in itertools.product(r.source.expand(), r.target.expand()):
|
||||
if s not in self.exclude and t not in self.exclude:
|
||||
self.__add_flow(s, t, r, wweight, rweight)
|
||||
for s, t in itertools.product(r.source.expand(), r.target.expand()):
|
||||
# only add flows if they actually flow
|
||||
# in or out of the source type type
|
||||
if s != t:
|
||||
if wweight:
|
||||
self.__add_edge(s, t, r, wweight)
|
||||
|
||||
if rweight:
|
||||
self.__add_edge(t, s, r, rweight)
|
||||
|
||||
self.rebuildgraph = False
|
||||
self.rebuildsubgraph = True
|
||||
|
||||
def _build_subgraph(self):
|
||||
if self.rebuildgraph:
|
||||
self._build_graph()
|
||||
|
||||
# delete excluded types from subgraph
|
||||
nodes = [n for n in self.G.nodes() if n not in self.exclude]
|
||||
self.subG = self.G.subgraph(nodes)
|
||||
|
||||
# delete edges below minimum weight
|
||||
delete_list = []
|
||||
for s, t, data in self.subG.edges_iter(data=True):
|
||||
if data['capacity'] < self.minweight:
|
||||
delete_list.append((s, t))
|
||||
|
||||
self.subG.remove_edges_from(delete_list)
|
||||
|
||||
self.rebuildsubgraph = False
|
||||
|
@ -25,14 +25,20 @@ from setools.permmap import PermissionMap
|
||||
from setools.policyrep.rule import RuleNotConditional
|
||||
|
||||
|
||||
# Note: the testing for having correct rules on every edge is only
|
||||
# performed once on the full graph, since it is assumed that NetworkX's
|
||||
# Digraph.subgraph() function correctly copies the edge attributes into
|
||||
# the subgraph.
|
||||
|
||||
|
||||
class InfoFlowAnalysisTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.p = SELinuxPolicy("tests/infoflow.conf")
|
||||
self.m = PermissionMap("tests/perm_map")
|
||||
|
||||
def test_001_no_minimum(self):
|
||||
"""Information flow analysis with no minimum weight."""
|
||||
def test_001_full_graph(self):
|
||||
"""Information flow analysis full graph."""
|
||||
|
||||
a = InfoFlowAnalysis(self.p, self.m)
|
||||
a._build_graph()
|
||||
@ -50,8 +56,9 @@ class InfoFlowAnalysisTest(unittest.TestCase):
|
||||
node9 = self.p.lookup_type("node9")
|
||||
|
||||
nodes = set(a.G.nodes_iter())
|
||||
self.assertSetEqual(set(
|
||||
[disconnected1, disconnected2, node1, node2, node3, node4, node5, node6, node7, node8, node9]), nodes)
|
||||
self.assertSetEqual(set([disconnected1, disconnected2, node1,
|
||||
node2, node3, node4, node5,
|
||||
node6, node7, node8, node9]), nodes)
|
||||
|
||||
edges = set(a.G.out_edges_iter())
|
||||
self.assertSetEqual(set([(disconnected1, disconnected2),
|
||||
@ -186,7 +193,7 @@ class InfoFlowAnalysisTest(unittest.TestCase):
|
||||
"""Information flow analysis with minimum weight 3."""
|
||||
|
||||
a = InfoFlowAnalysis(self.p, self.m, minweight=3)
|
||||
a._build_graph()
|
||||
a._build_subgraph()
|
||||
|
||||
disconnected1 = self.p.lookup_type("disconnected1")
|
||||
disconnected2 = self.p.lookup_type("disconnected2")
|
||||
@ -200,12 +207,14 @@ class InfoFlowAnalysisTest(unittest.TestCase):
|
||||
node8 = self.p.lookup_type("node8")
|
||||
node9 = self.p.lookup_type("node9")
|
||||
|
||||
nodes = set(a.G.nodes_iter())
|
||||
self.assertSetEqual(set([disconnected1, disconnected2, node1,
|
||||
node2, node3, node4, node5,
|
||||
node6, node7, node8, node9]), nodes)
|
||||
# don't test nodes, as disconnected nodes
|
||||
# are not removed by subgraph generation
|
||||
#nodes = set(a.subG.nodes_iter())
|
||||
#self.assertSetEqual(set([disconnected1, disconnected2, node1,
|
||||
# node2, node3, node4, node5,
|
||||
# node6, node7, node8, node9]), nodes)
|
||||
|
||||
edges = set(a.G.out_edges_iter())
|
||||
edges = set(a.subG.out_edges_iter())
|
||||
self.assertSetEqual(set([(disconnected1, disconnected2),
|
||||
(disconnected2, disconnected1),
|
||||
(node1, node2),
|
||||
@ -218,117 +227,11 @@ class InfoFlowAnalysisTest(unittest.TestCase):
|
||||
(node8, node9),
|
||||
(node9, node8)]), edges)
|
||||
|
||||
r = a.G.edge[disconnected1][disconnected2]["rules"]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "disconnected1")
|
||||
self.assertEqual(r[0].target, "disconnected2")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["super"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = a.G.edge[disconnected2][disconnected1]["rules"]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "disconnected1")
|
||||
self.assertEqual(r[0].target, "disconnected2")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["super"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge[node1][node2]["rules"])
|
||||
self.assertEqual(len(r), 2)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node1")
|
||||
self.assertEqual(r[0].target, "node2")
|
||||
self.assertEqual(r[0].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["med_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
self.assertEqual(r[1].ruletype, "allow")
|
||||
self.assertEqual(r[1].source, "node2")
|
||||
self.assertEqual(r[1].target, "node1")
|
||||
self.assertEqual(r[1].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["hi_r"]), r[1].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[1], "conditional")
|
||||
|
||||
r = sorted(a.G.edge[node1][node3]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node3")
|
||||
self.assertEqual(r[0].target, "node1")
|
||||
self.assertEqual(r[0].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["low_r", "med_r"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge[node2][node4]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node2")
|
||||
self.assertEqual(r[0].target, "node4")
|
||||
self.assertEqual(r[0].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["hi_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge[node4][node6]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node4")
|
||||
self.assertEqual(r[0].target, "node6")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["hi_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge[node5][node8]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node5")
|
||||
self.assertEqual(r[0].target, "node8")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["hi_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge[node6][node5]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node5")
|
||||
self.assertEqual(r[0].target, "node6")
|
||||
self.assertEqual(r[0].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["med_r"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge[node6][node7]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node6")
|
||||
self.assertEqual(r[0].target, "node7")
|
||||
self.assertEqual(r[0].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["hi_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge[node8][node9]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node8")
|
||||
self.assertEqual(r[0].target, "node9")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["super"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge[node9][node8]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node8")
|
||||
self.assertEqual(r[0].target, "node9")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["super"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
def test_200_minimum_8(self):
|
||||
"""Information flow analysis with minimum weight 8."""
|
||||
|
||||
a = InfoFlowAnalysis(self.p, self.m, minweight=8)
|
||||
a._build_graph()
|
||||
a._build_subgraph()
|
||||
|
||||
disconnected1 = self.p.lookup_type("disconnected1")
|
||||
disconnected2 = self.p.lookup_type("disconnected2")
|
||||
@ -342,12 +245,14 @@ class InfoFlowAnalysisTest(unittest.TestCase):
|
||||
node8 = self.p.lookup_type("node8")
|
||||
node9 = self.p.lookup_type("node9")
|
||||
|
||||
nodes = set(a.G.nodes_iter())
|
||||
self.assertSetEqual(set([disconnected1, disconnected2, node1,
|
||||
node2, node4, node5,
|
||||
node6, node7, node8, node9]), nodes)
|
||||
# don't test nodes, as disconnected nodes
|
||||
# are not removed by subgraph generation
|
||||
#nodes = set(a.subG.nodes_iter())
|
||||
#self.assertSetEqual(set([disconnected1, disconnected2, node1,
|
||||
# node2, node4, node5,
|
||||
# node6, node7, node8, node9]), nodes)
|
||||
|
||||
edges = set(a.G.out_edges_iter())
|
||||
edges = set(a.subG.out_edges_iter())
|
||||
self.assertSetEqual(set([(disconnected1, disconnected2),
|
||||
(disconnected2, disconnected1),
|
||||
(node1, node2),
|
||||
@ -357,84 +262,3 @@ class InfoFlowAnalysisTest(unittest.TestCase):
|
||||
(node6, node7),
|
||||
(node8, node9),
|
||||
(node9, node8)]), edges)
|
||||
|
||||
r = a.G.edge[disconnected1][disconnected2]["rules"]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "disconnected1")
|
||||
self.assertEqual(r[0].target, "disconnected2")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["super"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = a.G.edge[disconnected2][disconnected1]["rules"]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "disconnected1")
|
||||
self.assertEqual(r[0].target, "disconnected2")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["super"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge[node1][node2]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node2")
|
||||
self.assertEqual(r[0].target, "node1")
|
||||
self.assertEqual(r[0].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["hi_r"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge[node2][node4]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node2")
|
||||
self.assertEqual(r[0].target, "node4")
|
||||
self.assertEqual(r[0].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["hi_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge[node4][node6]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node4")
|
||||
self.assertEqual(r[0].target, "node6")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["hi_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge[node5][node8]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node5")
|
||||
self.assertEqual(r[0].target, "node8")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["hi_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge[node6][node7]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node6")
|
||||
self.assertEqual(r[0].target, "node7")
|
||||
self.assertEqual(r[0].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["hi_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge[node8][node9]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node8")
|
||||
self.assertEqual(r[0].target, "node9")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["super"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge[node9][node8]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node8")
|
||||
self.assertEqual(r[0].target, "node9")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["super"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
Loading…
Reference in New Issue
Block a user