Skip to content

Commit

Permalink
expressions: Ignore non-iterability of None in membership tests
Browse files Browse the repository at this point in the history
Python makes a distinction between something being contained in
something else (or not) and something not being able to be contained in
nothing (i.e. None) and raises a 'NoneType not iterable' exception in
the latter case. We can see no application of this distinction in our
use-case as of now. In fact it causes problems when writing tests
against object properties which are None. Therefore we handle that case
in our containment tests and interpret it as something (obviously) not
being contained in nothing.

Closes scVENUS#176.
  • Loading branch information
michaelweiser committed Mar 17, 2022
1 parent 740e2ac commit 9168dc5
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 6 deletions.
15 changes: 9 additions & 6 deletions peekaboo/ruleset/expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -448,16 +448,19 @@ def __init__(self, tokens):
}

@staticmethod
def in_(a, b):
def in_(op1, op2):
""" Literally implement membership test. Make it a static method so we
can do identity checks. Do not use operator.contains because it needs
operands swapped. """
return a in b
operands swapped. Also, there's no foreseeable use of the distinction
that something cannot be present in nothing in our application. So
prevent 'NoneType not iterable' exceptions by checking that op2 is not
None. """
return op2 is not None and op1 in op2

@staticmethod
def not_in(a, b):
""" Naively implement non-membership test. """
return a not in b
def not_in(op1, op2):
""" Implement non-membership test. """
return op2 is not None and op1 not in op2

@staticmethod
def and_(op1, op2):
Expand Down
11 changes: 11 additions & 0 deletions tests/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1297,6 +1297,7 @@ def test_rule_expression_filetools(self):
== "AppleDouble encoded Macintosh file" -> ignore
expression.1 : sample.file_extension in {"doc", "docx"}
and filereport.type_by_content != /application\/.*word/ -> bad
expression.2: "text" in filereport.type_by_name -> ignore
'''

sample_kwargs = {
Expand Down Expand Up @@ -1324,6 +1325,13 @@ def test_rule_expression_filetools(self):
result = rule.evaluate(sample)
self.assertEqual(result.result, Result.ignored)

# expression.2 should not raise a 'NoneType not iterable' exception due
# to type_by_name being None (and it should not match). This happens if
# the file name is empty, for example.
sample = Sample(b'dummy')
result = rule.evaluate(sample)
self.assertEqual(result.result, Result.unknown)

@asynctest
async def test_rule_expression_knowntools(self):
""" Test generic rule on knowntoolsreport. """
Expand Down Expand Up @@ -1931,6 +1939,9 @@ def test_basic_expressions(self):
["'foo' == 'bar'", False],
["'foo' == 'foo'", True],
["'foo' in 'bar'", False],
# we swallow None being non-iterable
["'foo' in None", False],
["'foo' not in None", False],
# re.search() for "match anywhere in operand"
["'foo' in 'foobar'", True],
["/foo/ in 'afoobar'", True],
Expand Down

0 comments on commit 9168dc5

Please sign in to comment.