Repository: oasis-open/cti-pattern-validator

Issue:
Unclear error message when missing brackets
If the input is missing the surrounding '[' and ']' (ie. the input is just a Comparison Expression, not an Observation Expression or Pattern Expresion), the validator's error message is unhelpful:

    $ validate_patterns
    Enter a pattern to validate: file-object:hashes.md5 = '79054025255fb1a26e4bc422aef54eb4'
    FAIL: Error found at line 1:0. no viable alternative at input 'file-object' 

This message comes from the ANTLR library so it can't easily be changed, but perhaps the validator could check if the input starts with '[' or '([' and print a more user-friendly error message?

Discussion:

Patch:
diff --git a/.travis.yml b/.travis.yml
index 896f07d..52cda7e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,10 +1,12 @@
 sudo: false
 language: python
 python:
+  - "2.6"
   - "2.7"
   - "3.3"
   - "3.4"
   - "3.5"
+  - "3.6"
 install:
   - pip install -U pip setuptools
   - pip install tox-travis
diff --git a/stix2patterns/validator.py b/stix2patterns/validator.py
index b1563d9..79313c0 100644
--- a/stix2patterns/validator.py
+++ b/stix2patterns/validator.py
@@ -35,9 +35,15 @@ def run_validator(pattern):
     returned in a list.  The test passed if the returned list is empty.
     '''
 
+    start = ''
     if isinstance(pattern, six.string_types):
+        start = pattern[:2]
         pattern = InputStream(pattern)
 
+    if not start:
+        start = pattern.readline()[:2]
+        pattern.seek(0)
+
     parseErrListener = STIXPatternErrorListener()
 
     lexer = STIXPatternLexer(pattern)
@@ -54,6 +60,11 @@ def run_validator(pattern):
 
     parser.pattern()
 
+    # replace with easier-to-understand error message
+    if not (start[0] == '[' or start == '(['):
+        parseErrListener.err_strings[0] = "FAIL: Error found at line 1:0. " \
+                                          "input is missing square brackets"
+
     return parseErrListener.err_strings
 
 
diff --git a/tox.ini b/tox.ini
index c40db2f..164fcf3 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-envlist = py27,py33,py34,py35,pycodestyle
+envlist = py26,py27,py33,py34,py35,py36,pycodestyle
 
 [testenv]
 deps = pytest
@@ -16,7 +16,9 @@ exclude=grammars
 
 [travis]
 python =
+  2.6: py26
   2.7: py27, pycodestyle
   3.3: py33
   3.4: py34
   3.5: py35
+  3.6: py36


Test patch:
diff --git a/stix2patterns/test/test_validator.py b/stix2patterns/test/test_validator.py
index 626b981..814517f 100644
--- a/stix2patterns/test/test_validator.py
+++ b/stix2patterns/test/test_validator.py
@@ -2,7 +2,6 @@
 Test cases for stix2patterns/validator.py.
 '''
 import os
-
 import pytest
 
 from stix2patterns.validator import validate
@@ -22,21 +21,33 @@ def test_spec_patterns(test_input):
 
 
 FAIL_CASES = [
-    "file:size = 1280",  # Does not use square brackets
-    "[file:hashes.MD5 = cead3f77f6cda6ec00f57d76c9a6879f]"  # No quotes around string
-    "[file.size = 1280]",  # Use period instead of colon
-    "[file:name MATCHES /.*\\.dll/]",  # Quotes around regular expression
-    "[win-registry-key:key = 'hkey_local_machine\\\\foo\\\\bar'] WITHIN 5 HOURS",  # SECONDS is the only valid time unit
+    ("file:size = 1280",  # Does not use square brackets
+        "FAIL: Error found at line 1:0. input is missing square brackets"),
+    ("[file:size = ]",  # Missing value
+        "FAIL: Error found at line 1:13. no viable alternative at input ']'"),
+    ("[file:hashes.MD5 = cead3f77f6cda6ec00f57d76c9a6879f]",  # No quotes around string
+        "FAIL: Error found at line 1:19. no viable alternative at input 'cead3f77f6cda6ec00f57d76c9a6879f'"),
+    ("[file.size = 1280]",  # Use period instead of colon
+        "FAIL: Error found at line 1:5. no viable alternative at input 'file.'"),
+    ("[file:name MATCHES /.*\\.dll/]",  # Quotes around regular expression
+        "FAIL: Error found at line 1:19. mismatched input '/' expecting <INVALID>"),
+    ("[win-registry-key:key = 'hkey_local_machine\\\\foo\\\\bar'] WITHIN ]",  # Missing Qualifier value
+        "FAIL: Error found at line 1:63. mismatched input ']' expecting {<INVALID>, <INVALID>}"),
+    ("[win-registry-key:key = 'hkey_local_machine\\\\foo\\\\bar'] WITHIN 5 HOURS]",  # SECONDS is the only valid time unit
+        "FAIL: Error found at line 1:65. mismatched input 'HOURS' expecting <INVALID>"),
+    ("[network-traffic:dst_ref.value ISSUBSET ]",  # Missing second Comparison operand
+        "FAIL: Error found at line 1:40. missing <INVALID> at ']'"),
     # TODO: add more failing test cases.
 ]
 
 
-@pytest.mark.parametrize("test_input", FAIL_CASES)
-def test_fail_patterns(test_input):
+@pytest.mark.parametrize("test_input,test_output", FAIL_CASES)
+def test_fail_patterns(test_input, test_output):
     '''
     Validate that patterns fail as expected.
     '''
-    pass_test = validate(test_input, print_errs=True)
+    pass_test, errors = validate(test_input, ret_errs=True, print_errs=True)
+    assert errors[0] == test_output
     assert pass_test is False