Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correct inclusion of parentheses when outputing constraints #133

Merged
merged 1 commit into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 31 additions & 18 deletions setools/policyrep/constraint.pxi
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Copyright 2014-2016, Tresys Technology, LLC
# Copyright 2016-2018, Chris PeBenito <[email protected]>
# Copyright 2024, Sealing Technologies, Inc.
#
# SPDX-License-Identifier: LGPL-2.1-only
#
Expand Down Expand Up @@ -192,40 +193,52 @@ cdef class ConstraintExpression(PolicyObject):

# sepol representation is in postfix notation. This code
# converts it to infix notation. Parentheses are added
# to ensure correct expressions, though they may end up
# being overused. Set previous operator at start to the
# highest precedence (op) so if there is a single binary
# operator, no parentheses are output
# to ensure correct expressions. Parentheses are needed
# whenever an operation involves a subexpression with
# lower precedence.
stack = []
prev_op_precedence = _max_precedence

@dataclasses.dataclass(repr=False, eq=False, frozen=True)
class StackObj:
precedence: int
expression: List[str]

for op in self._postfix:
if isinstance(op, frozenset) or op in _operands:
# operands
stack.append(op)
stack.append(StackObj(_max_precedence, op))
else:
# operators
op_precedence = _precedence[op]
if op == "not":
# unary operator
operator = op
operand = stack.pop()
op_precedence = _precedence[op]
stack.append([operator, "(", operand, ")"])
operand_info = stack.pop()
if operand_info.precedence < op_precedence:
e = [operator, "(", operand_info.expression, ")"]
else:
e = [operator, operand_info.expression]
else:
# binary operators
operand2 = stack.pop()
operand1 = stack.pop()
operand2_info = stack.pop()
operand1_info = stack.pop()
operator = op

# if previous operator is of higher precedence
# no parentheses are needed.
if _precedence[op] < prev_op_precedence:
stack.append([operand1, operator, operand2])
if operand1_info.precedence < op_precedence:
operand1 = ["(", operand1_info.expression, ")"]
else:
stack.append(["(", operand1, operator, operand2, ")"])
operand1 = [operand1_info.expression]

if operand2_info.precedence < op_precedence:
operand2 = ["(", operand2_info.expression, ")"]
else:
operand2 = [operand2_info.expression]

e = operand1 + [operator] + operand2

prev_op_precedence = _precedence[op]
stack.append(StackObj(op_precedence, e))

self._infix = flatten_list(stack)
self._infix = flatten_list(map(lambda x:x.expression, stack))

return self._infix

Expand Down
18 changes: 18 additions & 0 deletions tests/library/constraintquery.conf
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class test41b
class test50
class test51a
class test51b
class test52a
class test52b

sid kernel
sid security
Expand Down Expand Up @@ -123,6 +125,12 @@ inherits test
class test51b
inherits test

class test52a
inherits test

class test52b
inherits test

sensitivity low_s;
sensitivity medium_s alias med;
sensitivity high_s;
Expand Down Expand Up @@ -277,6 +285,16 @@ constrain test50 hi_w (u1 == u2 or u1 == test50u);
constrain test51a hi_w (u1 == u2 or u1 == test51u1);
constrain test51b hi_w (u1 == u2 or u2 == test51u2);

# test 52:
# ruletype: unset
# tclass: unset
# perms: unset
# role: unset
# type: unset
# user: unset
constrain test52a hi_w ((r1 == system or r2 == system) and u1 == u2);
constrain test52b hi_w (r1 == system or r2 == system and u1 == u2);

#isids
sid kernel system:system:system:medium_s:here
sid security system:system:system:high_s:lost
Expand Down
15 changes: 15 additions & 0 deletions tests/library/test_constraintquery.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Copyright 2015, Tresys Technology, LLC
# Copyright 2024, Sealing Technologies, Inc.
#
# SPDX-License-Identifier: GPL-2.0-only
#
Expand Down Expand Up @@ -94,3 +95,17 @@ def test_user_match_regex(self, compiled_policy: setools.SELinuxPolicy) -> None:

constraint = sorted(c.tclass for c in q.results())
assert ["test51a", "test51b"] == constraint

def test_or_and_parens(self, compiled_policy: setools.SELinuxPolicy) -> None:
"""Constraint with an or expression anded with another expression"""
q = setools.ConstraintQuery(compiled_policy, tclass=["test52a"])

constraint = sorted(str(c.expression) for c in q.results())
assert ["( r1 == system or r2 == system ) and u1 == u2"] == constraint

def test_or_and_no_parens(self, compiled_policy: setools.SELinuxPolicy) -> None:
"""Constraint with an or expression anded with another expression"""
q = setools.ConstraintQuery(compiled_policy, tclass=["test52b"])

constraint = sorted(str(c.expression) for c in q.results())
assert ["r1 == system or r2 == system and u1 == u2"] == constraint
Loading