Repository: duo-labs/parliament

Issue:
Policy variables in the resource element not supported
Hi, 
example policy:

```json
{
  "Version": "2012-10-17",
  "Statement": [
  {
      "Sid": "BucketAccess",
      "Effect": "Allow",
      "Action": [
        "s3:GetBucketLocation",
        "s3:GetBucketVersioning",
        "s3:GetEncryptionConfiguration",
        "s3:ListBucket",
        "s3:ListBucketMultipartUploads",
        "s3:ListBucketVersions"
      ],
      "Resource": "arn:aws:s3::bucket-for-client-${aws:PrincipalTag/Namespace}-*"
    }
]
}
```
Policy variable ${aws:PrincipalTag/Namespace} causes:
```json
{
    "description": "",
    "detail": [
        {
            "action": "s3:GetBucketLocation",
            "required_format": "arn:*:s3:::*"
        },
        {
            "action": "s3:GetBucketVersioning",
            "required_format": "arn:*:s3:::*"
        },
        {
            "action": "s3:GetEncryptionConfiguration",
            "required_format": "arn:*:s3:::*"
        },
        {
            "action": "s3:ListBucket",
            "required_format": "arn:*:s3:::*"
        },
        {
            "action": "s3:ListBucketMultipartUploads",
            "required_format": "arn:*:s3:::*"
        },
        {
            "action": "s3:ListBucketVersions",
            "required_format": "arn:*:s3:::*"
        }
    ],
    "issue": "RESOURCE_MISMATCH",
    "location": {
        "column": 3,
        "filepath": "main.json",
        "line": 4
    },
    "match": true,
    "severity": "MEDIUM",
    "title": "No resources match for the given action"
}
```

Maybe extending this condition https://github.com/duo-labs/parliament/blob/main/parliament/__init__.py#L118 by some regexp will be good enough for start ? ;) ( ex. (?P<Variable>\${aws:.*}) )

Take care!

Discussion:
0xdabbad00: Nice work @briandbecker! Thank you. This can be merged @steiza 

Patch:
diff --git a/parliament/__init__.py b/parliament/__init__.py
index 91dd4b3..1596023 100644
--- a/parliament/__init__.py
+++ b/parliament/__init__.py
@@ -115,7 +115,8 @@ def is_arn_match(resource_type, arn_format, resource):
 
     if "bucket" in resource_type:
         # We have to do a special case here for S3 buckets
-        if "/" in resource:
+        # and since resources can use variables which contain / need to replace them
+        if "/" in strip_var_from_arn(resource, "theVar"):
             return False
 
     # The ARN has at least 6 parts, separated by a colon. Ensure these exist.
@@ -144,7 +145,6 @@ def is_arn_match(resource_type, arn_format, resource):
 
     # Some of the arn_id's contain regexes of the form "[key]" so replace those with "*"
     resource_id = re.sub(r"\[.+?\]", "*", resource_id)
-
     return is_glob_match(arn_id, resource_id)
 
 def is_arn_strictly_valid(resource_type, arn_format, resource):
@@ -166,7 +166,6 @@ def is_arn_strictly_valid(resource_type, arn_format, resource):
     - resource: ARN regex from IAM policy
 
     """
-
     if is_arn_match(resource_type, arn_format, resource):
         # this would have already raised exception
         arn_parts = arn_format.split(":")
@@ -187,13 +186,16 @@ def is_arn_strictly_valid(resource_type, arn_format, resource):
                 return False
 
         # replace aws variable and check for other colons
-        resource_id_no_vars = re.sub(r"\$\{aws.\w+\}", "", resource_id)
+        resource_id_no_vars = strip_var_from_arn(resource_id)
         if ":" in resource_id_no_vars and not ":" in arn_id:
             return False
 
         return True
     return False
 
+def strip_var_from_arn(arn, replace_with=""):
+    return re.sub(r"\$\{aws.[\w\/]+\}", replace_with, arn)
+
 def is_glob_match(s1, s2):
     # This comes from https://github.com/duo-labs/parliament/issues/36#issuecomment-574001764
 
diff --git a/parliament/misc.py b/parliament/misc.py
index 08f8dac..a08c9bd 100644
--- a/parliament/misc.py
+++ b/parliament/misc.py
@@ -14,9 +14,11 @@ def make_list(v):
             location = jsoncfg.node_location(v)
             line = location.line
             column = location.column
-        else:
+        elif jsoncfg.node_exists(v):
             line = v.line
             column = v.column
+        else:
+            return []
 
         a = jsoncfg.config_classes.ConfigJSONArray(line, column)
         a._append(v)


Test patch:
diff --git a/tests/unit/test_resource_formatting.py b/tests/unit/test_resource_formatting.py
index 28a409c..0b9e7a6 100644
--- a/tests/unit/test_resource_formatting.py
+++ b/tests/unit/test_resource_formatting.py
@@ -90,6 +90,11 @@ class TestResourceFormatting(unittest.TestCase):
                 "arn:aws:logs:us-east-1:000000000000:/aws/cloudfront/test"
             )
         )
+        assert_true(
+            is_arn_match(
+                "bucket", "arn:*:s3:::*", "arn:aws:s3:::bucket-for-client-${aws:PrincipalTag/Namespace}-*"
+            )
+        )
 
     def test_is_arn_strictly_valid(self):
         assert_true(