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

Fix square bracket checking to account for arbitrary nested parentheses #81

Merged
merged 3 commits into from
Sep 23, 2020
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
37 changes: 22 additions & 15 deletions stix2patterns/helpers.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
import string
import six


def leading_characters(s, length):
def brackets_check(pattern):
"""
Returns non-whitespace leading characters
Check whether the pattern is missing square brackets, in a way which does
not require the usual parsing. This is a light hack to provide an improved
error message in this particular case.

:param str s: The string to process
:param int length: The number of characters to return
:return: The non-whitespace leading characters
:rtype: str or None
:param pattern: A STIX pattern string
:return: True if the pattern had its brackets; False if not
"""
if s is None:
return None
if isinstance(pattern, six.string_types):

stripped = []
for char in s:
if char not in string.whitespace:
stripped.append(char)
# There can be an arbitrary number of open parens first... skip over
# those
for c in pattern:
if c != "(" and not c.isspace():
break

upper_bound = min(length, len(stripped))
return ''.join(stripped[:upper_bound])
if c == "[":
result = True
else:
result = False

else:
result = False

return result
33 changes: 26 additions & 7 deletions stix2patterns/test/test_helpers.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,33 @@
"""
Test cases for stix2patterns/helpers.py.
"""
import pytest

from stix2patterns.helpers import leading_characters
from stix2patterns.helpers import brackets_check


def test_leading_characters():
@pytest.mark.parametrize(
"value", [
'[file:size = 1280]',
' [file:size = 1280]',
'( [file:size = 1280])',
'( ( [file:size = 1280]) )',
'(( ( ( [file:size = 1280])) ))',
'[',
],
)
def test_brackets_check(value):
assert brackets_check(value)

assert leading_characters('[file:size = 1280]', 2) == '[f'
assert leading_characters(' [file:size = 1280]', 2) == '[f'
assert leading_characters('( [file:size = 1280])', 2) == '(['
assert leading_characters('[', 2) == '['
assert leading_characters(None, 2) is None

@pytest.mark.parametrize(
"value", [
None,
"file:size = 1280",
"(file:size = 1280)",
" ( file:size = 1280 ) ",
" (( (( file:size = 1280 ) )) ) ",
]
)
def test_brackets_check_fail(value):
assert not brackets_check(None)
1 change: 1 addition & 0 deletions stix2patterns/v20/object_validator.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import re

import stix2patterns.inspector

HASHES_REGEX = {
Expand Down
7 changes: 1 addition & 6 deletions stix2patterns/v20/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from .inspector import InspectionListener


def run_validator(pattern, start):
def run_validator(pattern):
"""
Validates a pattern against the STIX Pattern grammar. Error messages are
returned in a list. The test passed if the returned list is empty.
Expand Down Expand Up @@ -40,11 +40,6 @@ def run_validator(pattern, start):
tree = parser.pattern()
inspection_listener = InspectionListener()

# replace with easier-to-understand error message
if not (start[0] == '[' or start == '(['):
parseErrListener.err_strings.insert(0, "FAIL: Error found at line 1:0. "
"input is missing square brackets")

# validate observed objects
if len(parseErrListener.err_strings) == 0:
ParseTreeWalker.DEFAULT.walk(inspection_listener, tree)
Expand Down
1 change: 1 addition & 0 deletions stix2patterns/v21/object_validator.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import re

import stix2patterns.inspector

HASHES_REGEX = {
Expand Down
7 changes: 1 addition & 6 deletions stix2patterns/v21/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def exitObservationExpressionStartStop(self, ctx):
self.__check_qualifier_type(QualType.STARTSTOP)


def run_validator(pattern, start):
def run_validator(pattern):
"""
Validates a pattern against the STIX Pattern grammar. Error messages are
returned in a list. The test passed if the returned list is empty.
Expand Down Expand Up @@ -94,11 +94,6 @@ def run_validator(pattern, start):

tree = parser.pattern()

# replace with easier-to-understand error message
if not (start[0] == '[' or start == '(['):
parseErrListener.err_strings.insert(0, "FAIL: Error found at line 1:0. "
"input is missing square brackets")

# validate observed objects
if len(parseErrListener.err_strings) == 0:
inspection_listener = InspectionListener()
Expand Down
21 changes: 14 additions & 7 deletions stix2patterns/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from . import DEFAULT_VERSION
from .exceptions import STIXPatternErrorListener # noqa: F401
from .helpers import leading_characters
from .helpers import brackets_check
from .v20.validator import run_validator as run_validator20
from .v21.validator import run_validator as run_validator21

Expand All @@ -21,19 +21,26 @@ def run_validator(pattern, stix_version=DEFAULT_VERSION):
Validates a pattern against the STIX Pattern grammar. Error messages are
returned in a list. The test passed if the returned list is empty.
"""
start = ''
if isinstance(pattern, six.string_types):
start = leading_characters(pattern, 2)
pattern_str = pattern
pattern = InputStream(pattern)

if not start:
start = leading_characters(pattern.readline(), 2)
else:
pattern_str = pattern.readline()
pattern.seek(0)

if stix_version == '2.1':
return run_validator21(pattern, start)
err_messages = run_validator21(pattern)
else:
return run_validator20(pattern, start)
err_messages = run_validator20(pattern)

if not brackets_check(pattern_str):
err_messages.insert(
0,
"FAIL: Error found at line 1:0. input is missing square brackets"
)

return err_messages


def validate(user_input, stix_version=DEFAULT_VERSION, ret_errs=False, print_errs=False):
Expand Down