Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
for key in principal:
if key == "AWS":
for aws_principal in make_list(principal[key]):
account_id_regex = re.compile("^\d{12}$")
arn_regex = re.compile("^arn:[-a-z\*]*:iam::(\d{12}|):.*$")
if aws_principal == "*":
pass
elif account_id_regex.match(aws_principal):
pass
elif arn_regex.match(aws_principal):
pass
else:
self.add_finding("UNKNOWN_PRINCIPAL", detail=aws_principal)
elif key == "Federated":
for federation in make_list(principal[key]):
saml_regex = re.compile(
"^arn:[-a-z\*]*:iam::\d{12}:saml-provider/.*$"
)
if federation in [
"cognito-identity.amazonaws.com",
"www.amazon.com",
"graph.facebook.com",
"accounts.google.com",
]:
pass
elif saml_regex.match(federation):
pass
else:
self.add_finding(
"UNKNOWN_FEDERATION_SOURCE", detail=federation
)
def in_actions(self, privilege_prefix, privilege_name):
"""
Given "s3" "GetObject", determine if the privilege is in this statement.
This could happen either because the Action is ["s3:GetObject"] or ["s3:*", "ec2:*"]
or because the action is not in the NotAction. For example, if we have an Allow on NotAction "ec2:*",
then this, with "s3" "GetObject" returns True.
"""
if "Action" in self.stmt:
for action in make_list(self.stmt["Action"]):
if action == "*" or action == "*:*":
return True
expanded_actions = expand_action(action, raise_exceptions=False)
for action_struct in expanded_actions:
if (
action_struct["service"] == privilege_prefix
and action_struct["action"] == privilege_name
):
return True
return False
# Else, we're dealing with a NotAction
for action in make_list(self.stmt["NotAction"]):
if action == "*" or action == "*:*":
if "Resource" in self.stmt:
resources = make_list(self.stmt["Resource"])
elif "NotResource" in self.stmt:
resources = make_list(self.stmt["NotResource"])
else:
self.add_finding(
"MALFORMED",
detail="Statement contains neither Resource nor NotResource",
location={"string": self.stmt},
)
return False
# Check if a Condition element exists and if so save them for later
if "Condition" in self.stmt:
conditions = make_list(self.stmt["Condition"])
if len(conditions) > 1:
self.add_finding(
"MALFORMED",
detail="Condition formatted incorrectly",
location={"string": self.stmt},
)
return False
# Expand the actions from s3:Get* to s3:GetObject and others
expanded_actions = []
for action in actions:
# Handle special case where all actions are allowed
if action == "*" or action == "*:*":
# TODO Should ensure the resource is "*" with this action
continue
return False
for key in condition_block:
# Check for known bad pattern
if operator.lower() == "bool":
if key.lower() == "aws:MultiFactorAuthPresent".lower() and "false" in make_list(
condition_block[key]
):
self.add_finding(
"BAD_PATTERN_FOR_MFA",
detail='The condition {"Bool": {"aws:MultiFactorAuthPresent":"false"}} is bad because aws:MultiFactorAuthPresent may not exist so it does not enforce MFA. You likely want to use a Deny with BoolIfExists.',
location={"location": condition_block},
)
elif operator.lower() == "null":
if key.lower == "aws:MultiFactorAuthPresent".lower() and "false" in make_list(
condition_block[key]
):
self.add_finding(
"BAD_PATTERN_FOR_MFA",
detail='The condition {"Null": {"aws:MultiFactorAuthPresent":"false"}} is bad because aws:MultiFactorAuthPresent it does not enforce MFA, and only checks if the value exists. You likely want to use an Allow with {"Bool": {"aws:MultiFactorAuthPresent":"true"}}.',
location={"location": condition_block},
)
if operator.lower() in ["null"]:
# The following condition is valid:
# "Condition": { "Null": { "aws:MultiFactorAuthAge": true }
# If we check further we'll get a MISMATCHED_TYPE finding due to
# aws:MultiFactorAuthAge being checked against a bool value instead of a date
continue
# The key here from the example is "s3:prefix"
privilege_info = get_privilege_info(privilege_prefix, privilege_name)
# Iterate through the resources defined in the action definition
for resource_type in privilege_info["resource_types"]:
resource_type = resource_type["resource_type"]
# Only check the required resources which have a "*" at the end
if "*" not in resource_type:
continue
arn_format = get_arn_format(
resource_type, privilege_info["service_resources"]
)
# At least one resource has to match the action's required resources
for resource in make_list(self.stmt["Resource"]):
if is_arn_match(resource_type, arn_format, resource):
affected_resources.append(resource)
elif resource == "*":
affected_resources.append(resource)
# Ensure we match on "*"
for resource in make_list(self.stmt["Resource"]):
if resource == "*":
affected_resources.append(resource)
return affected_resources
)
if operator.lower() in ["null"]:
# The following condition is valid:
# "Condition": { "Null": { "aws:MultiFactorAuthAge": true }
# If we check further we'll get a MISMATCHED_TYPE finding due to
# aws:MultiFactorAuthAge being checked against a bool value instead of a date
continue
# The key here from the example is "s3:prefix"
condition_type = get_global_key_type(key)
if condition_type:
# This is a global key, like aws:CurrentTime
# Check if the values match the type (ex. must all be Date values)
if not is_value_in_correct_format_for_type(
condition_type, make_list(condition_block[key])
):
self.add_finding(
"MISMATCHED_TYPE",
detail="Type mismatch: {} requires a value of type {} but given {}".format(
key, condition_type, condition_block[key]
),
location={"location": condition_block},
)
else:
# See if this is a service specific key
for action_struct in expanded_actions:
privilege_info = get_privilege_info(
action_struct["service"], action_struct["action"]
)
# Ensure the condition_key exists
def is_finding_filtered(finding, minimum_severity="LOW"):
# Return True if the finding should not be displayed
minimum_severity = minimum_severity.upper()
severity_choices = ["MUTE", "INFO", "LOW", "MEDIUM", "HIGH", "CRITICAL"]
if severity_choices.index(finding.severity) < severity_choices.index(
minimum_severity
):
return True
if finding.ignore_locations:
for location_type, locations_to_ignore in finding.ignore_locations.items():
for location_to_ignore in make_list(locations_to_ignore):
if (
location_to_ignore.lower()
in str(finding.location.get(location_type, "")).lower()
):
return True
return False
"Null operation requires being matched against true or false but given {}".format(
condition_block
),
severity.INVALID,
location={"location": condition_block},
)
return False
for key in condition_block:
# The key here from the example is "s3:prefix"
condition_type = get_global_key_type(key)
if condition_type:
# This is a global key, like aws:CurrentTime
# Check if the values match the type (ex. must all be Date values)
if not is_value_in_correct_format_for_type(
condition_type, make_list(condition_block[key])
):
self.add_finding(
"Type mismatch: {} requires a value of type {} but given {}".format(
key, condition_type, condition_block[key]
),
severity.INVALID,
location={"location": condition_block},
)
else:
# See if this is a service specific key
for action_struct in expanded_actions:
privilege_info = get_privilege_info(
action_struct["service"], action_struct["action"]
)
# Ensure the condition_key exists
if "Resource" in self.stmt:
resources = make_list(self.stmt["Resource"])
elif "NotResource" in self.stmt:
resources = make_list(self.stmt["NotResource"])
else:
self.add_finding(
"Statement contains neither Resource nor NotResource",
severity.MALFORMED,
location={"string": self.stmt},
)
return False
# Check if a Condition element exists and if so save them for later
if "Condition" in self.stmt:
conditions = make_list(self.stmt["Condition"])
if len(conditions) > 1:
self.add_finding(
"Condition formatted incorrectly",
severity.MALFORMED,
location={"string": self.stmt},
)
return False
# Expand the actions from s3:Get* to s3:GetObject and others
expanded_actions = []
for action in actions:
# Handle special case where all actions are allowed
if action == "*" or action == "*:*":
# TODO Should ensure the resource is "*" with this action
continue
if "Sid" in self.stmt and not re.fullmatch("[0-9A-Za-z]*", self.stmt["Sid"]):
# The grammar is defined at https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_grammar.html
self.add_finding("INVALID_SID", detail=self.stmt)
return False
# Check Action
if "Action" in self.stmt and "NotAction" in self.stmt:
self.add_finding(
"MALFORMED",
detail="Statement contains both Action and NotAction",
location={"string": self.stmt},
)
return False
if "Action" in self.stmt:
actions = make_list(self.stmt["Action"])
elif "NotAction" in self.stmt:
actions = make_list(self.stmt["NotAction"])
else:
self.add_finding(
"MALFORMED",
detail="Statement contains neither Action nor NotAction",
location={"string": self.stmt},
)
return False
# Check Resource exists and save the list of resources for later
if "Resource" in self.stmt and "NotResource" in self.stmt:
self.add_finding(
"MALFORMED",
detail="Statement contains both Resource and NotResource",
location={"string": self.stmt},