Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ Parses IAM identity-based and resource-based policies from Terraform templates.
| --enable-logging | | | Enables log output to stdout |
| --ignore-finding | | FINDING_CODE,RESOURCE_NAME,RESOURCE_NAME.FINDING_CODE | Allow validation failures to be ignored. Specify as a comma separated list of findings to be ignored. Can be individual finding codes (e.g. "PASS_ROLE_WITH_STAR_IN_RESOURCE"), a specific resource name (e.g. "MyResource"), or a combination of both separated by a period.(e.g. "MyResource.PASS_ROLE_WITH_STAR_IN_RESOURCE"). Names of finding codes may change in IAM Access Analyzer over time.
| --treat-finding-type-as-blocking | | ERROR,SECURITY_WARNING,WARNING,SUGGESTION,NONE | Specify which finding types should be treated as blocking. Other finding types are treated as nonblocking. If the tool detects any blocking finding types, it will exit with a non-zero exit code. If all findings are nonblocking or there are no findings, the tool exits with an exit code of 0. Defaults to "ERROR" and "SECURITY_WARNING". Specify as a comma separated list of finding types that should be blocking. Pass "NONE" to ignore all findings. |
| --treat-finding-code-as-blocking | | | Specify which finding codes should be treated as blocking. Other finding codes are treated as nonblocking. If the tool detects any blocking finding codes, it will exit with a non-zero exit code. If all findings are nonblocking or there are no findings, the tool exits with an exit code of 0. Not set by default. Specify as a comma separated list of finding codes that should be blocking. |
| --allow-external-principals | | ACCOUNT,ARN | A comma separated list of external principals that should be ignored. Specify as a comma separated list of a 12 digit AWS account ID, a federated web identity user, a federated SAML user, or an ARN. Specify "*" to allow anonymous access. (e.g. 123456789123,arn:aws:iam::111111111111:role/MyOtherRole,graph.facebook.com) |
| --config |Yes | FILE_NAME1, FILE_NAME2, ... | A list of config files for running this script |
**check-no-new-access**
Expand Down
31 changes: 31 additions & 0 deletions iam_check/argument_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,37 @@ def __call__(self, _, namespace, values, option_string=None):

setattr(namespace, self.dest, findings_to_ignore)

class ParseCodesToBlockFromCLI(argparse.Action):
"""
Parses comma delimited list of codes to block on. This is either a resource name or a finding code name, or
a combination of both in the form MyResource.FindingA
"""

def __call__(self, _, namespace, values, option_string=None):
values = values.split(',')

codes_to_block = parse_codes_to_block(values)

setattr(namespace, self.dest, codes_to_block)

def parse_codes_to_block(values_as_list):
if values_as_list is None:
return values_as_list

values_as_list = [value.strip() for value in values_as_list]

codes_to_block = []
for value in values_as_list:
if "." in value:
resource_and_code = value.split(".", 1)
# a split must have at least two members of the array, so no need to validate
finding_to_ignore = ResourceAndCodeFindingToBlock(resource_and_code[0], resource_and_code[1])
else:
finding_to_ignore = ResourceOrCodeFindingToBlock(value)

codes_to_block.append(finding_to_ignore)

return codes_to_block

def parse_findings_to_ignore(values_as_list):
if values_as_list is None:
Expand Down
5 changes: 5 additions & 0 deletions iam_check/iam_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ def cli_parse_opts():
'list of finding types that should be blocking. Possible values are "ERROR", '
'"SECURITY_WARNING", "SUGGESTION", and "WARNING". Pass "NONE" to ignore all errors.',
default=default_finding_types_that_are_blocking, type=validate_finding_types_from_cli)
validate_parser.add_argument('--treat-finding-code-as-blocking', dest="treat_as_blocking", metavar="ERROR,SECURITY_WARNING",
help='Specify which finding codes should be treated as blocking. Other finding codes are treated '
'as non-blocking. Not set by default. Specify as a comma separated '
'list of finding codes that should be blocking.',
default=default_finding_codes_that_are_blocking, type=validate_finding_codes_from_cli)

validate_parser.add_argument('--allow-external-principals', dest='allowed_external_principals', metavar="ACCOUNT,ARN",
help='A comma separated list of external principals that should be ignored. Specify as '
Expand Down
22 changes: 20 additions & 2 deletions iam_check/lib/reporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,21 @@
from iam_check.tools import regex_patterns

default_finding_types_that_are_blocking = ['ERROR', 'SECURITY_WARNING']
default_finding_codes_that_are_blocking = []


class Reporter:
"""
Determines what findings should be reported to the end user based on parameters provided when starting validation.
"""

def __init__(self, findings_to_ignore, finding_types_that_are_blocking, allowed_external_principals):
def __init__(self, findings_to_ignore, finding_types_that_are_blocking, allowed_external_principals, finding_codes_that_are_blocking=default_finding_codes_that_are_blocking):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

open to not doing a kwarg here but don't want to break existing implementations before a larger release

self.blocking_findings = []
self.nonblocking_findings = []
self.findings_to_ignore = findings_to_ignore
self.finding_types_that_are_blocking = finding_types_that_are_blocking
self.allowed_external_principals = allowed_external_principals
self.finding_codes_that_are_blocking = finding_codes_that_are_blocking

def build_report_from(self, findings):
self._filter_overridden_findings(findings)
Expand Down Expand Up @@ -66,6 +68,8 @@ def _classify_as_blocking_or_non_blocking(self, finding):

if finding.findingType.upper() in self.finding_types_that_are_blocking:
self.blocking_findings.append(finding)
elif finding.code.upper() in self.finding_codes_that_are_blocking:
self.blocking_findings.append(finding)
else:
self.nonblocking_findings.append(finding)

Expand All @@ -84,6 +88,20 @@ def __eq__(self, other):

return self.value == other.value

class ResourceOrCodeFindingToBlock:
def __init__(self, value):
self.value = value

def matches(self, finding):
return finding.resourceName.lower() == self.value.lower() or \
finding.code.lower() == self.value.lower()

def __eq__(self, other):
if not isinstance(other, ResourceOrCodeFindingToBlock):
return False

return self.value == other.value


class ResourceAndCodeFindingToIgnore:
def __init__(self, resource_name, code):
Expand Down Expand Up @@ -182,4 +200,4 @@ def print(self):

@staticmethod
def _to_json_string(obj):
return json.dumps(obj, default=default_to_json, indent=4)
return json.dumps(obj, default=default_to_json, indent=4)
25 changes: 24 additions & 1 deletion iam_check/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ def validate_finding_types_from_cli(value):

return finding_types

def validate_finding_codes_from_cli(value):
"""
Validate that the finding codes provided are valid finding codes.
"""

finding_codes = value.split(',')
finding_codes = validate_finding_codes(finding_codes)

return finding_codes


def validate_finding_types(finding_types):
if finding_types is None:
Expand All @@ -46,4 +56,17 @@ def validate_finding_types(finding_types):
if finding_type not in ['ERROR', 'SECURITY_WARNING', 'SUGGESTION', 'WARNING', 'NONE']:
raise ArgumentTypeError(f"Invalid finding type: {finding_type}.")

return finding_types
return finding_types

def validate_finding_codes(finding_codes):
if finding_codes is None:
return finding_codes

finding_codes = [finding_code.strip() for finding_code in finding_codes]
finding_codes = [finding_code.upper() for finding_code in finding_codes]

# for finding_code in finding_codes:
# if finding_code not in ['ERROR', 'SECURITY_WARNING', 'SUGGESTION', 'WARNING', 'NONE']:
# raise ArgumentTypeError(f"Invalid finding code: {finding_code}.")
Comment on lines +68 to +70
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there any way to pull all possible finding codes? otherwise validation is just "does it split"


return finding_codes
Loading