"""Terraform static code analysis tool using Checkov. This tool scans Github pull-request to find new, moved and updated .tf and .tfvars files. Then the tool runs checkov scan against the list of directories that contain the changed files. The parsed output gets printed to console and sent to Github as pull-request comments. """ import os import sys import filter import output import pull_request import scanner REPO_DIR = os.getenv('REPO_DIR') GITHUB_API_KEY = os.getenv('GITHUB_API_KEY') GITHUB_APP_CLIENT_ID = os.getenv('GITHUB_APP_CLIENT_ID') GITHUB_APP_INSTALLATION_ID = int(os.getenv('GITHUB_APP_INSTALLATION_ID', '0')) GITHUB_APP_PRIVATE_KEY_FILE_LOCATION = os.getenv( 'GITHUB_APP_PRIVATE_KEY_FILE_LOCATION') if GITHUB_APP_PRIVATE_KEY_FILE_LOCATION: GITHUB_APP_PRIVATE_KEY = open(GITHUB_APP_PRIVATE_KEY_FILE_LOCATION).read() GITHUB_REPO_NAME = os.getenv('GITHUB_REPO_NAME') GITHUB_PR_NUM = os.getenv('GITHUB_PR_NUM') RECURSIVE_SCAN = os.getenv('RECURSIVE_SCAN', 'true') BLOCKING_MODE = os.getenv('BLOCKING_MODE', 'false') AWS_ROLES_PER_PREFIX = os.getenv('AWS_ROLES_PER_PREFIX', '') if __name__ == '__main__': print('Checking environment variables') if not REPO_DIR: sys.exit('Error: you need to specify REPO_DIR environment variable ' 'to point to a git repository.') if (not (GITHUB_API_KEY or (GITHUB_APP_PRIVATE_KEY and GITHUB_APP_CLIENT_ID and GITHUB_APP_INSTALLATION_ID)) or not GITHUB_REPO_NAME or not GITHUB_PR_NUM): sys.exit('Error: you need to specify GITHUB_REPO_NAME ' 'and GITHUB_PR_NUM environment variables, as well as ' 'one of either GITHUB_API_KEY or GITHUB_APP_PRIVATE_KEY, ' 'GITHUB_APP_CLIENT_ID, and GITHUB_APP_INSTALLATION_ID to ' 'analyze and add comments to Github pull-requests.') RECURSIVE_SCAN = True if RECURSIVE_SCAN == 'true' else False BLOCKING_MODE = True if BLOCKING_MODE == 'true' else False print('Processing list of AWS cross-account access roles') aws_roles = {} if AWS_ROLES_PER_PREFIX: for role_prefix in AWS_ROLES_PER_PREFIX.split(','): prefix, role = role_prefix.split('=') aws_roles[prefix] = role print('Checking repository directory') repo_dir = os.path.abspath(REPO_DIR) if not os.path.isdir(repo_dir): sys.exit(f'Error: repository directory "{REPO_DIR}" not found') print('Checking Github pull-request') if GITHUB_API_KEY: pr = pull_request.PullRequest( GITHUB_REPO_NAME, int(GITHUB_PR_NUM), api_key=GITHUB_API_KEY ) elif GITHUB_APP_CLIENT_ID and GITHUB_APP_PRIVATE_KEY: pr = pull_request.PullRequest( GITHUB_REPO_NAME, int(GITHUB_PR_NUM), app_client_id=GITHUB_APP_CLIENT_ID, app_installation_id=GITHUB_APP_INSTALLATION_ID, app_private_key=GITHUB_APP_PRIVATE_KEY ) if BLOCKING_MODE: gh_out = output.GithubStatusCheckOutput(pr) else: gh_out = output.GithubOutput(pr) out = output.OutputCollection([output.ConsoleOutput(), gh_out]) print('Analyzing Github pull-request changes') dirs = pr.get_directories( status_filter=['added', 'modified', 'renamed'], extension_filter=['.tf', '.tfvars', '.json'], merge_recursive=RECURSIVE_SCAN) if not dirs: print('Hiding old comments') pr.hide_old_comments() out.success('This pull request does not contain any changes to run ' 'a scan.') pr.set_status('success', 'Checkov scan passed: no files to scan') sys.exit(0) print('Running scan with checkov, recursive scan is {}'.format( 'enabled' if RECURSIVE_SCAN else 'disabled')) checks = [] for scan_dir in dirs: print(f'Scanning "{scan_dir}"') aws_role = None for prefix, role in aws_roles.items(): if not scan_dir.startswith(prefix+os.sep): continue aws_role = role items = scanner.scan(repo_dir, pr.token, scan_dir, aws_role) for item in items: checks.append(item) print('Filtering scan results') if not RECURSIVE_SCAN: checks = filter.remove_recursive_findings(checks, dirs) checks = filter.prioritize_module_update(checks) print('Hiding old comments') pr.hide_old_comments() print('Processing scan results\n') if not checks: out.success('The scan has been completed successfully without any ' 'issues being spotted.') pr.set_status('success', 'Checkov scan passed') sys.exit(0) for check in checks: out.finding(check) out.flush() pr.set_status('failure', 'Checkov scan failed: issues found') print('Done')