async def post(self): """ POST /api/v2/policies/check """ policy = tornado.escape.json_decode(self.request.body) analyzed_policy = analyze_policy_string(policy) findings = analyzed_policy.findings enhanced_findings = [] for finding in findings: enhanced_finding = enhance_finding(finding) enhanced_findings.append({ "issue": enhanced_finding.issue, "detail": enhanced_finding.detail, "location": enhanced_finding.location, "severity": enhanced_finding.severity, "title": enhanced_finding.title, "description": enhanced_finding.description, }) self.write(json.dumps(enhanced_findings)) return
def is_finding_filtered(finding, minimum_severity="LOW"): # Return True if the finding should not be displayed minimum_severity = minimum_severity.upper() finding = enhance_finding(finding) severity_choices = ["MUTE", "INFO", "LOW", "MEDIUM", "HIGH", "CRITICAL"] if severity_choices.index( finding.severity) < severity_choices.index(minimum_severity): return True return False
def main(): parser = argparse.ArgumentParser() parser.add_argument( "--aws-managed-policies", help= "This is used with the policies directory of https://github.com/SummitRoute/aws_managed_policies", type=str, ) parser.add_argument( "--auth-details-file", help= 'Provide the path to a file returned by "aws iam get-account-authorization-details"', type=str, ) parser.add_argument( "--string", help= 'Provide a string such as \'{"Version": "2012-10-17","Statement": {"Effect": "Allow","Action": ["s3:GetObject", "s3:PutBucketPolicy"],"Resource": ["arn:aws:s3:::bucket1", "arn:aws:s3:::bucket2/*"]}}\'', type=str, ) parser.add_argument("--file", help="Provide a policy in a file", type=str) parser.add_argument("--directory", help="Provide a path to directory with policy files", type=str) parser.add_argument( "--include_policy_extension", help= 'Policy file extension to scan for in directory mode (ex. ".json")', default="json", type=str, ) parser.add_argument( "--exclude_pattern", help='File name regex pattern to exclude (ex. ".*venv.*")', type=str, ) parser.add_argument("--minimal", help="Minimal output", default=False, action="store_true") parser.add_argument("--json", help="json output", default=False, action="store_true") parser.add_argument( "--minimum_severity", help= "Minimum severity to display. Options: CRITICAL, HIGH, MEDIUM, LOW, INFO", default="LOW", choices=["CRITICAL", "HIGH", "MEDIUM", "LOW", "INFO"], ) parser.add_argument( "--private_auditors", help= "Directory of the private auditors. Defaults to looking in private_auditors in the same directory as the iam_definition.json file.", default=None, ) parser.add_argument("--config", help="Custom config file for over-riding values", type=str) parser.add_argument( "--include-community-auditors", help="Use this flag to enable community-provided auditors", default=None, action="store_true", ) parser.add_argument( "-v", "--verbose", help="Increase output verbosity", action="store_true", default=False, ) parser.add_argument( "--version", action="version", version="%(prog)s {version}".format(version=__version__), ) args = parser.parse_args() log_level = logging.ERROR log_format = "%(message)s" # We want to silence dependencies logging.getLogger("botocore").setLevel(logging.CRITICAL) logging.getLogger("boto3").setLevel(logging.CRITICAL) logging.getLogger("urllib3").setLevel(logging.CRITICAL) if args.verbose: log_level = logging.INFO log_format = "%(message)s" logging.basicConfig(level=log_level, stream=sys.stderr, format=log_format) if args.private_auditors is not None and "." in args.private_auditors: raise Exception( "The path to the private_auditors must not have periods") if args.minimal and args.json: raise Exception("You cannot choose both minimal and json output") # Change the exit status if there are errors exit_status = 0 findings = [] if args.aws_managed_policies: file_paths = find_files(directory=args.aws_managed_policies) for file_path in file_paths: with open(file_path) as f: contents = f.read() policy_file_json = json.loads(contents) policy_string = json.dumps( policy_file_json["PolicyVersion"]["Document"]) policy = analyze_policy_string( policy_string, file_path, private_auditors_custom_path=args.private_auditors, include_community_auditors=args.include_community_auditors, ) findings.extend(policy.findings) elif args.auth_details_file: with open(args.auth_details_file) as f: contents = f.read() auth_details_json = json.loads(contents) for policy in auth_details_json["Policies"]: # Ignore AWS defined policies if "arn:aws:iam::aws:" not in policy["Arn"]: continue # Ignore AWS Service-linked roles if (policy["Path"] == "/service-role/" or policy["Path"] == "/aws-service-role/" or policy["PolicyName"].startswith("AWSServiceRoleFor") or policy["PolicyName"].endswith("ServiceRolePolicy") or policy["PolicyName"].endswith( "ServiceLinkedRolePolicy")): continue for version in policy["PolicyVersionList"]: if not version["IsDefaultVersion"]: continue policy = analyze_policy_string( json.dumps(version["Document"]), policy["Arn"], ) findings.extend(policy.findings) # Review the inline policies on Users, Roles, and Groups for user in auth_details_json["UserDetailList"]: for policy in user.get("UserPolicyList", []): policy = analyze_policy_string( json.dumps(policy["PolicyDocument"]), user["Arn"], private_auditors_custom_path=args.private_auditors, include_community_auditors=args. include_community_auditors, ) findings.extend(policy.findings) for role in auth_details_json["RoleDetailList"]: for policy in role.get("RolePolicyList", []): policy = analyze_policy_string( json.dumps(policy["PolicyDocument"]), role["Arn"], private_auditors_custom_path=args.private_auditors, include_community_auditors=args. include_community_auditors, ) findings.extend(policy.findings) for group in auth_details_json["GroupDetailList"]: for policy in group.get("GroupPolicyList", []): policy = analyze_policy_string( json.dumps(policy["PolicyDocument"]), group["Arn"], private_auditors_custom_path=args.private_auditors, include_community_auditors=args. include_community_auditors, ) findings.extend(policy.findings) elif args.string: policy = analyze_policy_string( args.string, private_auditors_custom_path=args.private_auditors, include_community_auditors=args.include_community_auditors, ) findings.extend(policy.findings) elif args.file: with open(args.file) as f: contents = f.read() policy = analyze_policy_string( contents, args.file, private_auditors_custom_path=args.private_auditors, include_community_auditors=args.include_community_auditors, ) findings.extend(policy.findings) elif args.directory: file_paths = find_files( directory=args.directory, exclude_pattern=args.exclude_pattern, policy_extension=args.include_policy_extension, ) for file_path in file_paths: with open(file_path) as f: contents = f.read() policy = analyze_policy_string( contents, file_path, private_auditors_custom_path=args.private_auditors, include_community_auditors=args.include_community_auditors, ) findings.extend(policy.findings) else: parser.print_help() exit(-1) filtered_findings = [] override_config(args.config) if args.include_community_auditors: community_auditors_directory = "community_auditors" community_auditors_override_file = (Path(abspath(__file__)).parent / community_auditors_directory / "config_override.yaml") override_config(community_auditors_override_file) for finding in findings: finding = enhance_finding(finding) if not is_finding_filtered(finding, args.minimum_severity): filtered_findings.append(finding) if len(filtered_findings) == 0: # Return with exit code 0 if no findings return for finding in filtered_findings: print_finding(finding, args.minimal, args.json) # There were findings, so return with a non-zero exit code exit(1)
print("Checking {}".format(filename)) findings = validate_file(filename) if args.config: try: parliament.override_config(args.config) except FileNotFoundError as e: if args.config_is_optional: print("Config file {} not found. Ignoring.".format( args.config)) else: raise e filtered_findings = [] for finding in findings: finding = parliament.enhance_finding(finding) if not is_finding_filtered(finding): filtered_findings.append(finding) if len(filtered_findings) > 0: print(f"{bcolors.FAIL}{filename}{bcolors.ENDC}") for f in filtered_findings: print(format_finding(f)) findings_found = True print() elif args.verbose: print(f"{bcolors.OKGREEN}{filename} VALID{bcolors.ENDC}") # If we found any findings, in any files, exit code non-zero if findings_found: sys.exit(1) else:
def main(): parser = argparse.ArgumentParser() parser.add_argument( "--aws-managed-policies", help= "This is used with the policies directory of https://github.com/SummitRoute/aws_managed_policies", type=str, ) parser.add_argument( "--auth-details-file", help= 'Provide the path to a file returned by "aws iam get-account-authorization-details"', type=str, ) parser.add_argument( "--string", help= 'Provide a string such as \'{"Version": "2012-10-17","Statement": {"Effect": "Allow","Action": ["s3:GetObject", "s3:PutBucketPolicy"],"Resource": ["arn:aws:s3:::bucket1", "arn:aws:s3:::bucket2/*"]}}\'', type=str, ) parser.add_argument("--file", help="Provide a policy in a file", type=str) parser.add_argument("--minimal", help="Minimal output", default=False, action="store_true") parser.add_argument("--json", help="json output", default=False, action="store_true") parser.add_argument( "--minimum_severity", help= "Minimum severity to display. Options: CRITICAL, HIGH, MEDIUM, LOW, INFO", default="LOW", ) parser.add_argument( "--private_auditors", help= "Directory of the private auditors. Defaults to looking in private_auditors in the same directory as the iam_definition.json file.", default=None, ) parser.add_argument("--config", help="Custom config file for over-riding values", type=str) args = parser.parse_args() if args.private_auditors is not None and "." in args.private_auditors: raise Exception( "The path to the private_auditors must not have periods") if args.minimal and args.json: raise Exception("You cannot choose both minimal and json output") # Change the exit status if there are errors exit_status = 0 findings = [] if args.aws_managed_policies: filenames = [ f for f in listdir(args.aws_managed_policies) if isfile(join(args.aws_managed_policies, f)) ] for filename in filenames: filepath = join(args.aws_managed_policies, filename) with open(filepath) as f: contents = f.read() policy_file_json = json.loads(contents) policy_string = json.dumps( policy_file_json["PolicyVersion"]["Document"]) policy = analyze_policy_string( policy_string, filepath, private_auditors_custom_path=args.private_auditors, ) findings.extend(policy.findings) elif args.auth_details_file: with open(args.auth_details_file) as f: contents = f.read() auth_details_json = json.loads(contents) for policy in auth_details_json["Policies"]: # Ignore AWS defined policies if "arn:aws:iam::aws:" not in policy["Arn"]: continue for version in policy["PolicyVersionList"]: if not version["IsDefaultVersion"]: continue policy = analyze_policy_string( json.dumps(version["Document"]), policy["Arn"], ) findings.extend(policy.findings) # Review the inline policies on Users, Roles, and Groups for user in auth_details_json["UserDetailList"]: for policy in user.get("UserPolicyList", []): policy = analyze_policy_string( json.dumps(version["Document"]), user["Arn"], private_auditors_custom_path=args.private_auditors, ) findings.extend(policy.findings) for role in auth_details_json["RoleDetailList"]: for policy in role.get("RolePolicyList", []): policy = analyze_policy_string( json.dumps(version["Document"]), role["Arn"], private_auditors_custom_path=args.private_auditors, ) findings.extend(policy.findings) for group in auth_details_json["GroupDetailList"]: for policy in group.get("GroupPolicyList", []): policy = analyze_policy_string( json.dumps(version["Document"]), group["Arn"], private_auditors_custom_path=args.private_auditors, ) findings.extend(policy.findings) elif args.string: policy = analyze_policy_string( args.string, private_auditors_custom_path=args.private_auditors) findings.extend(policy.findings) elif args.file: with open(args.file) as f: contents = f.read() policy = analyze_policy_string( contents, args.file, private_auditors_custom_path=args.private_auditors) findings.extend(policy.findings) else: parser.print_help() exit(-1) filtered_findings = [] override_config(args.config) for finding in findings: finding = enhance_finding(finding) if not is_finding_filtered(finding, args.minimum_severity): filtered_findings.append(finding) if len(filtered_findings) == 0: # Return with exit code 0 if no findings return for finding in filtered_findings: print_finding(finding, args.minimal, args.json) # There were findings, so return with a non-zero exit code exit(1)