Skip to content

Commit 305c8fa

Browse files
authored
Merge pull request #133 from marshalljmiller/main
Correct inclusion of parentheses when outputing constraints
2 parents 42aad09 + 2ea10f1 commit 305c8fa

3 files changed

Lines changed: 64 additions & 18 deletions

File tree

setools/policyrep/constraint.pxi

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Copyright 2014-2016, Tresys Technology, LLC
22
# Copyright 2016-2018, Chris PeBenito <pebenito@ieee.org>
3+
# Copyright 2024, Sealing Technologies, Inc.
34
#
45
# SPDX-License-Identifier: LGPL-2.1-only
56
#
@@ -192,40 +193,52 @@ cdef class ConstraintExpression(PolicyObject):
192193

193194
# sepol representation is in postfix notation. This code
194195
# converts it to infix notation. Parentheses are added
195-
# to ensure correct expressions, though they may end up
196-
# being overused. Set previous operator at start to the
197-
# highest precedence (op) so if there is a single binary
198-
# operator, no parentheses are output
196+
# to ensure correct expressions. Parentheses are needed
197+
# whenever an operation involves a subexpression with
198+
# lower precedence.
199199
stack = []
200-
prev_op_precedence = _max_precedence
200+
201+
@dataclasses.dataclass(repr=False, eq=False, frozen=True)
202+
class StackObj:
203+
precedence: int
204+
expression: List[str]
205+
201206
for op in self._postfix:
202207
if isinstance(op, frozenset) or op in _operands:
203208
# operands
204-
stack.append(op)
209+
stack.append(StackObj(_max_precedence, op))
205210
else:
206211
# operators
212+
op_precedence = _precedence[op]
207213
if op == "not":
208214
# unary operator
209215
operator = op
210-
operand = stack.pop()
211-
op_precedence = _precedence[op]
212-
stack.append([operator, "(", operand, ")"])
216+
operand_info = stack.pop()
217+
if operand_info.precedence < op_precedence:
218+
e = [operator, "(", operand_info.expression, ")"]
219+
else:
220+
e = [operator, operand_info.expression]
213221
else:
214222
# binary operators
215-
operand2 = stack.pop()
216-
operand1 = stack.pop()
223+
operand2_info = stack.pop()
224+
operand1_info = stack.pop()
217225
operator = op
218226

219-
# if previous operator is of higher precedence
220-
# no parentheses are needed.
221-
if _precedence[op] < prev_op_precedence:
222-
stack.append([operand1, operator, operand2])
227+
if operand1_info.precedence < op_precedence:
228+
operand1 = ["(", operand1_info.expression, ")"]
223229
else:
224-
stack.append(["(", operand1, operator, operand2, ")"])
230+
operand1 = [operand1_info.expression]
231+
232+
if operand2_info.precedence < op_precedence:
233+
operand2 = ["(", operand2_info.expression, ")"]
234+
else:
235+
operand2 = [operand2_info.expression]
236+
237+
e = operand1 + [operator] + operand2
225238

226-
prev_op_precedence = _precedence[op]
239+
stack.append(StackObj(op_precedence, e))
227240

228-
self._infix = flatten_list(stack)
241+
self._infix = flatten_list(map(lambda x:x.expression, stack))
229242

230243
return self._infix
231244

tests/library/constraintquery.conf

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ class test41b
2121
class test50
2222
class test51a
2323
class test51b
24+
class test52a
25+
class test52b
2426

2527
sid kernel
2628
sid security
@@ -123,6 +125,12 @@ inherits test
123125
class test51b
124126
inherits test
125127

128+
class test52a
129+
inherits test
130+
131+
class test52b
132+
inherits test
133+
126134
sensitivity low_s;
127135
sensitivity medium_s alias med;
128136
sensitivity high_s;
@@ -277,6 +285,16 @@ constrain test50 hi_w (u1 == u2 or u1 == test50u);
277285
constrain test51a hi_w (u1 == u2 or u1 == test51u1);
278286
constrain test51b hi_w (u1 == u2 or u2 == test51u2);
279287

288+
# test 52:
289+
# ruletype: unset
290+
# tclass: unset
291+
# perms: unset
292+
# role: unset
293+
# type: unset
294+
# user: unset
295+
constrain test52a hi_w ((r1 == system or r2 == system) and u1 == u2);
296+
constrain test52b hi_w (r1 == system or r2 == system and u1 == u2);
297+
280298
#isids
281299
sid kernel system:system:system:medium_s:here
282300
sid security system:system:system:high_s:lost

tests/library/test_constraintquery.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Copyright 2015, Tresys Technology, LLC
2+
# Copyright 2024, Sealing Technologies, Inc.
23
#
34
# SPDX-License-Identifier: GPL-2.0-only
45
#
@@ -94,3 +95,17 @@ def test_user_match_regex(self, compiled_policy: setools.SELinuxPolicy) -> None:
9495

9596
constraint = sorted(c.tclass for c in q.results())
9697
assert ["test51a", "test51b"] == constraint
98+
99+
def test_or_and_parens(self, compiled_policy: setools.SELinuxPolicy) -> None:
100+
"""Constraint with an or expression anded with another expression"""
101+
q = setools.ConstraintQuery(compiled_policy, tclass=["test52a"])
102+
103+
constraint = sorted(str(c.expression) for c in q.results())
104+
assert ["( r1 == system or r2 == system ) and u1 == u2"] == constraint
105+
106+
def test_or_and_no_parens(self, compiled_policy: setools.SELinuxPolicy) -> None:
107+
"""Constraint with an or expression anded with another expression"""
108+
q = setools.ConstraintQuery(compiled_policy, tclass=["test52b"])
109+
110+
constraint = sorted(str(c.expression) for c in q.results())
111+
assert ["r1 == system or r2 == system and u1 == u2"] == constraint

0 commit comments

Comments
 (0)