Repository: oasis-open/cti-pattern-validator

Issue:
Validator raises/crashes on hashes type "wildcard subscription"
Steps to reproduce
------------------
```
$ validate-patterns -v 2.1
Enter a pattern to validate: [file:hashes[*] = '8665c8d477534008b3058b72e2dae8ae']
```

Expected result
---------------
Something like `FAIL: Error found at line 1:... hashes type does not support wildcard subscription`

Actual result
-------------
```
Traceback (most recent call last):
  File "bin/validate-patterns", line 10, in <module>
    sys.exit(main())
  File "lib/python3.6/site-packages/stix2patterns/validator.py", line 91, in main
    tests_passed, err_strings = validate(pattern, args.version, True)
  File "lib/python3.6/site-packages/stix2patterns/validator.py", line 47, in validate
    errs = run_validator(user_input, stix_version)
  File "lib/python3.6/site-packages/stix2patterns/validator.py", line 34, in run_validator
    return run_validator21(pattern, start)
  File "lib/python3.6/site-packages/stix2patterns/v21/validator.py", line 109, in run_validator
    obj_validator_results = object_validator.verify_object(patt_data)
  File "lib/python3.6/site-packages/stix2patterns/v21/object_validator.py", line 29, in verify_object
    hash_type = str(expression[0][-1].upper().replace('-', '').
AttributeError: 'object' object has no attribute 'upper'
```


Discussion:

Patch:
diff --git a/stix2patterns/v20/object_validator.py b/stix2patterns/v20/object_validator.py
index 235ee63..72f051c 100644
--- a/stix2patterns/v20/object_validator.py
+++ b/stix2patterns/v20/object_validator.py
@@ -1,4 +1,5 @@
 import re
+import stix2patterns.inspector
 
 HASHES_REGEX = {
     "MD5": (r"^[a-fA-F0-9]{32}$", "MD5"),
@@ -25,14 +26,16 @@ def verify_object(patt_data):
 
     # iterate over observed objects
     for type_name, comp in patt_data.comparisons.items():
-        for expression in comp:
-            if 'hashes' in expression[0]:
-                hash_type = str(expression[0][-1].upper().replace('-', '').
-                                replace("\'", ""))
-                hash_string = str(expression[2].replace("\'", ""))
-                if hash_type in HASHES_REGEX:
-                    if not re.match(HASHES_REGEX[hash_type][0], hash_string):
-                        error_list.append(
-                            msg.format(hash_string, expression[0][-1])
-                        )
+        for obj_path, op, value in comp:
+            if 'hashes' in obj_path:
+                hash_selector = obj_path[-1]
+                if hash_selector is not stix2patterns.inspector.INDEX_STAR:
+                    hash_type = \
+                        hash_selector.upper().replace('-', '').replace("'", "")
+                    hash_string = value.replace("'", "")
+                    if hash_type in HASHES_REGEX:
+                        if not re.match(HASHES_REGEX[hash_type][0], hash_string):
+                            error_list.append(
+                                msg.format(hash_string, hash_selector)
+                            )
     return error_list
diff --git a/stix2patterns/v21/object_validator.py b/stix2patterns/v21/object_validator.py
index 9920862..7558ba5 100644
--- a/stix2patterns/v21/object_validator.py
+++ b/stix2patterns/v21/object_validator.py
@@ -1,4 +1,5 @@
 import re
+import stix2patterns.inspector
 
 HASHES_REGEX = {
     "MD5": (r"^[a-fA-F0-9]{32}$", "MD5"),
@@ -25,14 +26,16 @@ def verify_object(patt_data):
 
     # iterate over observed objects
     for type_name, comp in patt_data.comparisons.items():
-        for expression in comp:
-            if 'hashes' in expression[0]:
-                hash_type = str(expression[0][-1].upper().replace('-', '').
-                                replace("\'", ""))
-                hash_string = str(expression[2].replace("\'", ""))
-                if hash_type in HASHES_REGEX:
-                    if not re.match(HASHES_REGEX[hash_type][0], hash_string):
-                        error_list.append(
-                            msg.format(hash_string, expression[0][-1])
-                        )
+        for obj_path, op, value in comp:
+            if 'hashes' in obj_path:
+                hash_selector = obj_path[-1]
+                if hash_selector is not stix2patterns.inspector.INDEX_STAR:
+                    hash_type = \
+                        hash_selector.upper().replace('-', '').replace("'", "")
+                    hash_string = value.replace("'", "")
+                    if hash_type in HASHES_REGEX:
+                        if not re.match(HASHES_REGEX[hash_type][0], hash_string):
+                            error_list.append(
+                                msg.format(hash_string, hash_selector)
+                            )
     return error_list


Test patch:
diff --git a/stix2patterns/test/v20/test_validator.py b/stix2patterns/test/v20/test_validator.py
index 44e0eae..8ddfa65 100644
--- a/stix2patterns/test/v20/test_validator.py
+++ b/stix2patterns/test/v20/test_validator.py
@@ -87,6 +87,7 @@ PASS_CASES = [
     "[foo:bar=1] REPEATS 9 TIMES",
     "[network-traffic:start = '2018-04-20T12:36:24.558Z']",
     "( [(network-traffic:dst_port IN(443,6443,8443) AND network-traffic:src_packets != 0) ])",  # Misplaced whitespace
+    "[file:hashes[*] = '8665c8d477534008b3058b72e2dae8ae']",
 ]
 
 
diff --git a/stix2patterns/test/v21/test_validator.py b/stix2patterns/test/v21/test_validator.py
index 26f9076..d6e2ef5 100644
--- a/stix2patterns/test/v21/test_validator.py
+++ b/stix2patterns/test/v21/test_validator.py
@@ -100,7 +100,8 @@ PASS_CASES = [
     "[network-traffic:src_port = 37020 AND user-account:user_id = 'root'] "
     "START t'2016-06-01T01:30:00.123Z' STOP t'2016-06-01T02:20:00.123Z' OR "
     "[ipv4-addr:value = '192.168.122.83'] "
-    "START t'2016-06-01T03:55:00.123Z' STOP t'2016-06-01T04:30:24.743Z'"
+    "START t'2016-06-01T03:55:00.123Z' STOP t'2016-06-01T04:30:24.743Z'",
+    "[file:hashes[*] = '8665c8d477534008b3058b72e2dae8ae']",
 ]