From a28c69966e41694c072e4016387c747178b56859 Mon Sep 17 00:00:00 2001 From: Andrew Hoying Date: Mon, 12 Mar 2018 10:44:48 -0700 Subject: [PATCH] Regex match (#1244) * Make regex match non-greedy to ensure matched string matches policy. --- .../security/common/gcp_type/iam_policy.py | 24 +++---------------- .../cloud/security/common/util/regex_util.py | 6 ++++- tests/common/gcp_type/iam_policy_test.py | 6 ++++- 3 files changed, 13 insertions(+), 23 deletions(-) diff --git a/google/cloud/security/common/gcp_type/iam_policy.py b/google/cloud/security/common/gcp_type/iam_policy.py index 1bd78d263f..6bfd00c800 100755 --- a/google/cloud/security/common/gcp_type/iam_policy.py +++ b/google/cloud/security/common/gcp_type/iam_policy.py @@ -19,25 +19,7 @@ import re from google.cloud.security.common.gcp_type import errors - - -# TODO: use the regex_util -def _escape_and_globify(pattern_string): - """Given a pattern string with a glob, create actual regex pattern. - - To require > 0 length glob, change the "*" to ".+". This is to handle - strings like "*@company.com". (THe actual regex would probably be - ".*@company.com", except that we don't want to match zero-length - usernames before the "@".) - - Args: - pattern_string (str): The pattern string of which to make a regex. - - Returns: - str: The pattern string, escaped except for the "*", which is - transformed into ".+" (match on one or more characters). - """ - return '^{}$'.format(re.escape(pattern_string).replace('\\*', '.+')) +from google.cloud.security.common.util.regex_util import escape_and_globify def _get_iam_members(members): @@ -137,7 +119,7 @@ def __init__(self, role_name, members=None): 'role_name={}, members={}'.format(role_name, members))) self.role_name = role_name self.members = _get_iam_members(members) - self.role_pattern = re.compile(_escape_and_globify(role_name), + self.role_pattern = re.compile(escape_and_globify(role_name), flags=re.IGNORECASE) def __eq__(self, other): @@ -217,7 +199,7 @@ def __init__(self, member_type, member_name=None): self.name = member_name self.name_pattern = None if member_name: - self.name_pattern = re.compile(_escape_and_globify(self.name), + self.name_pattern = re.compile(escape_and_globify(self.name), flags=re.IGNORECASE) def __eq__(self, other): diff --git a/google/cloud/security/common/util/regex_util.py b/google/cloud/security/common/util/regex_util.py index ced6bf7c96..a9735fea44 100644 --- a/google/cloud/security/common/util/regex_util.py +++ b/google/cloud/security/common/util/regex_util.py @@ -24,6 +24,8 @@ def escape_and_globify(pattern_string): ".*@company.com", except that we don't want to match zero-length usernames before the "@".) + Special case the pattern '*' to match 0 or more characters. + Args: pattern_string (str): The pattern string of which to make a regex. @@ -31,4 +33,6 @@ def escape_and_globify(pattern_string): str: The pattern string, escaped except for the "*", which is transformed into ".+" (match on one or more characters). """ - return '^{}$'.format(re.escape(pattern_string).replace('\\*', '.*')) + if pattern_string == '*': + return '^.*$' + return '^{}$'.format(re.escape(pattern_string).replace('\\*', '.+?')) diff --git a/tests/common/gcp_type/iam_policy_test.py b/tests/common/gcp_type/iam_policy_test.py index ab9add4f30..abc778c4b1 100644 --- a/tests/common/gcp_type/iam_policy_test.py +++ b/tests/common/gcp_type/iam_policy_test.py @@ -92,6 +92,10 @@ def test_member_match_works(self): 'user:not-user@company.com')) self.assertFalse(iam_policy_members[2].matches( 'user:anyone@not.company.com')) + self.assertFalse(iam_policy_members[2].matches( + 'user:anyone@notmycompany.com')) + self.assertFalse(iam_policy_members[2].matches( + 'user:anyone@mycompany.com.notmycompany.com')) self.assertFalse(iam_policy_members[3].matches( 'serviceAccount:someone@gserviceaccount.com')) self.assertFalse(iam_policy_members[3].matches( @@ -123,7 +127,7 @@ def test_binding_create_from_is_correct(self): 'members': self.test_members } iam_binding2 = IamPolicyBinding.create_from(binding2) - self.assertEqual('^roles\/.+$', iam_binding2.role_pattern.pattern) + self.assertEqual('^roles\/.+?$', iam_binding2.role_pattern.pattern) def test_binding_missing_role_raises(self): """Test that a binding with no role raises an exception."""