def __init__( self, policy_name, arn, actions, policy_document, exclusions=DEFAULT_EXCLUSIONS, assume_role_policy_document=None, attached_managed_policies=None, attached_to_principal=None, ): self.policy_name = policy_name self.arn = arn # The `scan` command uses this, whereas the `scan-policy-file` one does not. if "/" in self.arn: self.name = get_resource_string(self.arn).split("/")[1] else: self.name = policy_name self.type = capitalize_first_character( get_resource_string(self.arn).split("/")[0]) self.always_exclude_actions = exclusions.exclude_actions self.exclusions = exclusions self.actions = self._actions(actions) self.policy_document = policy_document # Roles only self.assume_role_policy_document = assume_role_policy_document # # Users only # self.group_membership = self._group_membership(group_membership) # Principals only (not policies) self.attached_managed_policies = self._attached_managed_policies( attached_managed_policies) self.attached_to_principal = attached_to_principal
def json(self): """Return the JSON representation of the Finding. This is used in the report output and the results data file.""" result = { "AccountID": self.account_id, "ManagedBy": self.managed_by, "PolicyName": self.policy_name, "Type": capitalize_first_character(get_resource_string(self.arn).split("/")[0]), "Arn": self.arn, # "ActionsCount": self.actions_count, # "ServicesCount": self.services_count, "ActionsCount": len(self.actions), "ServicesCount": len(self.services_affected), "Services": self.services_affected, "Actions": self.actions, "PolicyDocument": self.policy_document.json, "AssumeRolePolicyDocument": self.assume_role_policy_document_json, # These items help with prioritizing triage and remediation. "AssumableByComputeService": self.role_assumable_by_compute_services, "PrivilegeEscalation": self.privilege_escalation, "DataExfiltrationActions": self.data_leak_actions, "PermissionsManagementActions": self.permissions_management_actions_without_constraints, # Separate the "Write" and "Tagging" actions in the machine-readable output only # "WriteActions": self.policy_document.write_actions_without_constraints, # "TaggingActions": self.policy_document.tagging_actions_without_constraints, } return result
def create_triage_worksheet(account_name, results, output_directory): """ Create a triage spreadsheet for account owners to fill out. They can specify whether it is a false positive depending on context or whether or not it needs to be fixed. """ triage_spreadsheet_file = os.path.join(output_directory, f"iam-triage-{account_name}.csv") csv_fieldnames = [ "PolicyName", "Type", "ManagedBy", "Services", # ServicesCount "Actions", # ActionsCount "Justification", ] if os.path.exists(triage_spreadsheet_file): os.remove(triage_spreadsheet_file) with open(triage_spreadsheet_file, "w", newline="") as csv_file: writer = csv.DictWriter(csv_file, fieldnames=csv_fieldnames) writer.writeheader() # Write customer findings first for finding in results: finding_type = get_resource_string(finding["Arn"]).split("/")[0] if finding["ManagedBy"] == "Customer": if finding_type.lower() == "policy": this_finding_type = "Customer-Managed Policy" else: this_finding_type = f"Inline {finding_type.capitalize()} Policy" entry = { "PolicyName": finding["PolicyName"], "Type": this_finding_type, "ManagedBy": finding["ManagedBy"], "Services": finding["ServicesCount"], "Actions": finding["ActionsCount"], "Justification": "", } writer.writerow(entry) # Write AWS findings second for finding in results: if finding["ManagedBy"] == "AWS": entry = { "PolicyName": finding["PolicyName"], "Type": "AWS-Managed Policy", "ManagedBy": finding["ManagedBy"], "Services": finding["ServicesCount"], "Actions": finding["ActionsCount"], "Justification": "", } writer.writerow(entry) print( f"Use this spreadsheet to triage results: {triage_spreadsheet_file}" )
def resources(self) -> [ListResourcesResponse]: """Get a list of these resources""" these_resources = [] paginator = self.client.get_paginator('list_topics') page_iterator = paginator.paginate() for page in page_iterator: resources = page["Topics"] for resource in resources: arn = resource.get("TopicArn") name = get_resource_string(arn) list_resources_response = ListResourcesResponse( service=self.service, account_id=self.current_account_id, arn=arn, region=self.region, resource_type=self.resource_type, name=name) these_resources.append(list_resources_response) return these_resources