refactor(Prowler): Main logic refactor (#1189)

* fix(aws_profile_loader): New functions

* fix(shellcheck): Temporary remove Shellcheck

* fix(aws_cli_detector): new function

* fix(jq_detector): New function

* fix(os_detector): New function

* fix(output_bucket): Output bucket input check in main

* fix(python_detector): deleted unused python detector

* fix(credentials): credentials check out of whoami

* [break]refactor(main)

* [BREAK] Get list of checks parsing all input options

* [break]refactor(main): execute checks functions

* [break]refactor(main): move functions to libs

* fix(validations): custom check validation and typos

* refactor(validate_options): Include comments

* fix(custom_checks): Minor fixes

* refactor(closing_files): include libraries

* refactor(loader): Include ignored checks

* refactor(main): Fix shellcheck

* refactor(loader): beautify

* refactor(monochrome): without variables

* refactor(modes): MODES array not needed

* refactor(whoami): get error from AWSCLI

* refactor(secrets-detector)

* refactor(secrets-detector)

* fix(html_scoring): html scoring was fixed.

* fix(load_checks_from_file)

* fix(color-code): Print if not mono

* fix(not extra): Fixed if EXCLUDE_CHECK_ID is empty

* fix(IFS): Restore default IFS once modes are parsed

* fix(bucket): validate before whoami

* fix(bucket): validate before whoami

Co-authored-by: n4ch04 <nachor1992@gmail.com>
Co-authored-by: sergargar <sergio@verica.io>
Co-authored-by: Nacho Rivera <59198746+n4ch04@users.noreply.github.com>
This commit is contained in:
Pepe Fagoaga
2022-06-13 17:34:31 +02:00
committed by GitHub
parent 74a9b42d9f
commit da000b54ca
51 changed files with 1120 additions and 1048 deletions
+4 -4
View File
@@ -12,7 +12,7 @@ repos:
- id: pretty-format-json
args: ['--autofix']
- repo: https://github.com/koalaman/shellcheck-precommit
rev: v0.8.0
hooks:
- id: shellcheck
# - repo: https://github.com/koalaman/shellcheck-precommit
# rev: v0.8.0
# hooks:
# - id: shellcheck
-1
View File
@@ -14,7 +14,6 @@
GROUP_ID[10]='hipaa'
GROUP_NUMBER[10]='10.0'
GROUP_TITLE[10]='HIPAA Compliance - ONLY AS REFERENCE - [hipaa] ****************'
GROUP_RUN_BY_DEFAULT[10]='N' # run it when execute_all is called
GROUP_CHECKS[10]='check12,check113,check23,check26,check27,check29,extra718,extra725,extra72,extra75,extra717,extra729,extra734,check38,extra73,extra740,extra735,check112,check13,check15,check16,check17,check18,check19,check21,check24,check28,check31,check310,check311,check312,check313,check314,check32,check33,check34,check35,check36,check37,check39,extra792'
# Resources:
+1 -3
View File
@@ -14,9 +14,7 @@
GROUP_ID[11]='secrets'
GROUP_NUMBER[11]='11.0'
GROUP_TITLE[11]='Look for keys secrets or passwords around resources - [secrets]'
GROUP_RUN_BY_DEFAULT[11]='N' # but it runs when execute_all is called (default)
GROUP_CHECKS[11]='extra741,extra742,extra759,extra760,extra768,extra775,extra7141'
# requires https://github.com/Yelp/detect-secrets
# `pip install detect-secrets`
# `pip install detect-secrets`
-2
View File
@@ -14,6 +14,4 @@
GROUP_ID[12]='apigateway'
GROUP_NUMBER[12]='12.0'
GROUP_TITLE[12]='API Gateway security checks - [apigateway] ********************'
GROUP_RUN_BY_DEFAULT[12]='N' # run it when execute_all is called
GROUP_CHECKS[12]='extra722,extra743,extra744,extra745,extra746'
-1
View File
@@ -14,5 +14,4 @@
GROUP_ID[13]='rds'
GROUP_NUMBER[13]='13.0'
GROUP_TITLE[13]='RDS security checks - [rds] ***********************************'
GROUP_RUN_BY_DEFAULT[13]='N' # run it when execute_all is called
GROUP_CHECKS[13]='extra78,extra723,extra735,extra739,extra747,extra7113,extra7131,extra7132,extra7133'
+1 -2
View File
@@ -14,5 +14,4 @@
GROUP_ID[14]='elasticsearch'
GROUP_NUMBER[14]='14.0'
GROUP_TITLE[14]='Elasticsearch related security checks - [elasticsearch] *******'
GROUP_RUN_BY_DEFAULT[14]='N' # run it when execute_all is called
GROUP_CHECKS[14]='extra715,extra716,extra779,extra780,extra781,extra782,extra783,extra784,extra785,extra787,extra788,extra7101'
GROUP_CHECKS[14]='extra715,extra716,extra779,extra780,extra781,extra782,extra783,extra784,extra785,extra787,extra788,extra7101'
-1
View File
@@ -14,7 +14,6 @@
GROUP_ID[15]='pci'
GROUP_NUMBER[15]='15.0'
GROUP_TITLE[15]='PCI-DSS v3.2.1 Readiness - ONLY AS REFERENCE - [pci] **********'
GROUP_RUN_BY_DEFAULT[15]='N' # run it when execute_all is called
GROUP_CHECKS[15]='check11,check12,check13,check14,check15,check16,check17,check18,check19,check110,check112,check113,check114,check116,check21,check23,check25,check26,check27,check28,check29,check314,check36,check38,check43,extra711,extra713,extra717,extra718,extra72,extra729,extra735,extra738,extra740,extra744,extra748,extra75,extra750,extra751,extra753,extra754,extra755,extra773,extra78,extra780,extra781,extra782,extra783,extra784,extra785,extra787,extra788,extra798'
# Resources:
-1
View File
@@ -14,7 +14,6 @@
GROUP_ID[16]='trustboundaries'
GROUP_NUMBER[16]='16.0'
GROUP_TITLE[16]='Find cross-account trust boundaries - [trustboundaries] *******'
GROUP_RUN_BY_DEFAULT[16]='N' # run it when execute_all is called
GROUP_CHECKS[16]='extra789,extra790'
# Single account environment: No action required. The AWS account number will be automatically added by the checks.
-1
View File
@@ -14,7 +14,6 @@
GROUP_ID[17]='internet-exposed'
GROUP_NUMBER[17]='17.0'
GROUP_TITLE[17]='Find resources exposed to the internet - [internet-exposed] ***'
GROUP_RUN_BY_DEFAULT[17]='N' # run it when execute_all is called
GROUP_CHECKS[17]='check41,check42,check45,check46,extra72,extra73,extra74,extra76,extra77,extra78,extra79,extra710,extra711,extra716,extra723,extra727,extra731,extra736,extra738,extra745,extra748,extra749,extra750,extra751,extra752,extra753,extra754,extra755,extra770,extra771,extra778,extra779,extra787,extra788,extra795,extra796,extra798,extra7102,extra7134,extra7135,extra7136,extra7137,extra7138'
# 4.1 [check41] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to port 22 (Scored) [group4, cislevel1, cislevel2]
+2 -3
View File
@@ -14,7 +14,6 @@
GROUP_ID[18]='iso27001'
GROUP_NUMBER[18]='18.0'
GROUP_TITLE[18]='ISO 27001:2013 Readiness - ONLY AS REFERENCE - [iso27001] *****'
GROUP_RUN_BY_DEFAULT[18]='N' # run it when execute_all is called
GROUP_CHECKS[18]='check11,check110,check111,check112,check113,check114,check115,check116,check119,check12,check122,check13,check14,check15,check16,check17,check18,check19,check21,check22,check23,check24,check25,check26,check27,check28,check29,check31,check310,check311,check312,check313,check314,check32,check33,check34,check35,check36,check37,check38,check39,check41,check42,check43,check44,extra71,extra710,extra7100,extra711,extra7113,extra7123,extra7125,extra7126,extra7128,extra7129,extra713,extra714,extra7130,extra718,extra719,extra72,extra720,extra721,extra722,extra723,extra724,extra725,extra726,extra727,extra728,extra729,extra731,extra73,extra731,extra735,extra739,extra74,extra741,extra747,extra748,extra75,extra757,extra758,extra759,extra76,extra760,extra761,extra762,extra763,extra764,extra765,extra767,extra768,extra769,extra77,extra771,extra772,extra774,extra776,extra777,extra778,extra78,extra789,extra79,extra790,extra792,extra793,extra794,extra795,extra796,extra798'
# # Category Objective ID Objective Name Prowler check ID Check Summary
@@ -103,7 +102,7 @@ GROUP_CHECKS[18]='check11,check110,check111,check112,check113,check114,check115,
# 83 A.12 Operations Security A.12.4 Logging and Monitoring check39 Ensure a log metric filter and alarm exist for AWS Config configuration changes
# 84 A.12 Operations Security A.12.4 Logging and Monitoring check39 Check if CloudFront distributions have logging enabled
# 85 A.12 Operations Security A.12.4 Logging and Monitoring extra719 Check if Route53 public hosted zones are logging queries to CloudWatch Logs
# 86 A.12 Operations Security A.12.4 Logging and Monitoring extra720 Check if Lambda functions invoke API operations are being recorded by CloudTrail
# 86 A.12 Operations Security A.12.4 Logging and Monitoring extra720 Check if Lambda functions invoke API operations are being recorded by CloudTrail
# 87 A.12 Operations Security A.12.4 Logging and Monitoring extra722 Check if API Gateway has logging enabled
# 88 A.12 Operations Security A.12.4 Logging and Monitoring check38 Ensure a log metric filter and alarm exist for S3 bucket policy changes
# 89 A.12 Operations Security A.12.4 Logging and Monitoring check37 Ensure a log metric filter and alarm exist for disabling or scheduled deletion of customer created CMKs
@@ -136,7 +135,7 @@ GROUP_CHECKS[18]='check11,check110,check111,check112,check113,check114,check115,
#116 A.12 Operations Security A.12.6 Technical Vulnerability Management check23 Ensure the S3 bucket CloudTrail logs to is not publicly accessible
#117 A.12 Operations Security A.12.6 Technical Vulnerability Management extra713 Check if GuardDuty is enabled
#118 A.12 Operations Security A.12.6 Technical Vulnerability Management extra726 Check Trusted Advisor for errors and warnings
#119 A.12 Operations Security A.12.6 Technical Vulnerability Management extra776 Check if ECR image scan found vulnerabilities in the newest image version
#119 A.12 Operations Security A.12.6 Technical Vulnerability Management extra776 Check if ECR image scan found vulnerabilities in the newest image version
#120 A.13 Communications Security A.13.1 Network Security Management check43 Ensure the default security group of every VPC restricts all traffic
#121 A.13 Communications Security A.13.1 Network Security Management check42 Ensure no security groups allow ingress from 0.0.0.0/0 to port 3389
#122 A.13 Communications Security A.13.1 Network Security Management check41 Ensure no security groups allow ingress from 0.0.0.0/0 to port 22
-1
View File
@@ -11,5 +11,4 @@
GROUP_ID[19]='eks-cis'
GROUP_NUMBER[19]='19.0'
GROUP_TITLE[19]='CIS EKS Benchmark - [eks-cis] *********************************'
GROUP_RUN_BY_DEFAULT[19]='N' # run it when execute_all is called
GROUP_CHECKS[19]='extra765,extra794,extra795,extra796,extra797'
-1
View File
@@ -11,5 +11,4 @@
GROUP_ID[1]='group1'
GROUP_NUMBER[1]='1.0'
GROUP_TITLE[1]='Identity and Access Management - CIS only - [group1] ***********'
GROUP_RUN_BY_DEFAULT[1]='Y' # run it when execute_all is called
GROUP_CHECKS[1]='check11,check12,check13,check14,check15,check16,check17,check18,check19,check110,check111,check112,check113,check114,check115,check116,check117,check118,check119,check120,check121,check122'
-1
View File
@@ -14,7 +14,6 @@
GROUP_ID[20]='ffiec'
GROUP_NUMBER[20]='20.0'
GROUP_TITLE[20]='FFIEC Cybersecurity Readiness - ONLY AS REFERENCE - [ffiec] ***'
GROUP_RUN_BY_DEFAULT[20]='N' # run it when execute_all is called
GROUP_CHECKS[20]='check11,check12,check13,check14,check16,check18,check19,check21,check23,check25,check29,check29,check31,check32,check33,check34,check35,check36,check37,check37,check38,check39,check41,check42,check43,check110,check112,check113,check116,check310,check311,check312,check313,check314,extra72,extra76,extra78,extra711,extra723,extra729,extra731,extra734,extra735,extra763,extra792'
# References:
-1
View File
@@ -14,7 +14,6 @@
GROUP_ID[21]='soc2'
GROUP_NUMBER[21]='21.0'
GROUP_TITLE[21]='SOC2 Readiness - ONLY AS REFERENCE - [soc2] *******************'
GROUP_RUN_BY_DEFAULT[21]='N' # run it when execute_all is called
GROUP_CHECKS[21]='check110,check111,check113,check12,check122,check13,check15,check16,check17,check18,check19,check21,check31,check310,check32,check33,check34,check35,check36,check37,check38,check39,check41,check42,check43,extra711,extra72,extra723,extra729,extra731,extra734,extra735,extra739,extra76,extra78,extra792'
# References:
-2
View File
@@ -14,6 +14,4 @@
GROUP_ID[22]='sagemaker'
GROUP_NUMBER[22]='22.0'
GROUP_TITLE[22]='Amazon SageMaker related security checks - [sagemaker] ********'
GROUP_RUN_BY_DEFAULT[22]='N' # run it when execute_all is called
GROUP_CHECKS[22]='extra7103,extra7104,extra7111,extra7112,extra7105,extra7106,extra7107,extra7108,extra7109,extra7110'
-1
View File
@@ -14,7 +14,6 @@
GROUP_ID[23]='ens'
GROUP_NUMBER[23]='23.0'
GROUP_TITLE[23]='ENS Esquema Nacional de Seguridad security checks - [ens] *****'
GROUP_RUN_BY_DEFAULT[23]='N' # run it when execute_all is called
GROUP_CHECKS[23]='extra733,extra7123,check13,check14,check121,extra7100,check120,check116,extra7124,check12,extra7125,check14,check13,check21,check25,extra7127,check35,check24,check31,check36,check32,check33,check34,check22,extra71,check23,check23,check27,check37,extra736,check28,extra713,check21,check29,extra793,extra792,extra764,extra738,check43,extra74,extra710,extra75,check41,check42,extra749,extra750,extra751,extra752,extra753,extra754,extra755,extra7128,extra729,extra761,extra740,extra735,extra734,extra728,extra781,extra773,extra744,extra7126,extra7129'
# ENS Control ID for AWS;Prowler checks that apply
+1 -2
View File
@@ -14,5 +14,4 @@
GROUP_ID[24]='glue'
GROUP_NUMBER[24]='24.0'
GROUP_TITLE[24]='Amazon Glue related security checks - [glue] ******************'
GROUP_RUN_BY_DEFAULT[24]='N' # run it when execute_all is called
GROUP_CHECKS[24]='extra7114,extra7115,extra7116,extra7117,extra7118,extra7119,extra7120,extra7121,extra7122'
GROUP_CHECKS[24]='extra7114,extra7115,extra7116,extra7117,extra7118,extra7119,extra7120,extra7121,extra7122'
-2
View File
@@ -14,7 +14,6 @@
GROUP_ID[25]='ftr'
GROUP_NUMBER[25]='25.0'
GROUP_TITLE[25]='Amazon FTR related security checks - [ftr] ********************'
GROUP_RUN_BY_DEFAULT[25]='N' # run it when execute_all is called
GROUP_CHECKS[25]='check11,check12,check13,check14,check15,check16,check17,check18,check19,check110,check111,check111,check112,check113,check117,check118,check122,check21,check22,extra759,extra760,extra768,extra775,extra797,extra7141,extra73'
# Checks from AWS FTR https://apn-checklists.s3.amazonaws.com/foundational/partner-hosted/partner-hosted/CVLHEC5X7.html
@@ -43,4 +42,3 @@ GROUP_CHECKS[25]='check11,check12,check13,check14,check15,check16,check17,check1
# 7.97 [extra797] Ensure Kubernetes Secrets are encrypted using Customer Master Keys (CMKs) - eks [Medium]
# 7.141 [extra7141] Find secrets in SSM Documents - ssm [Critical]
# 7.3 [extra73] Ensure there are no S3 buckets open to Everyone or Any AWS user - s3 [Critical]
+2 -3
View File
@@ -13,6 +13,5 @@
GROUP_ID[26]='ds'
GROUP_NUMBER[26]='26.0'
GROUP_TITLE[26]='Amazon Directory Service related security checks - [ds] ******************'
GROUP_RUN_BY_DEFAULT[26]='N' # run it when execute_all is called
GROUP_CHECKS[26]='extra7181,extra7182,extra7183,extra7184'
GROUP_TITLE[26]='Amazon Directory Service related security checks - [ds] *******'
GROUP_CHECKS[26]='extra7181,extra7182,extra7183,extra7184'
-1
View File
@@ -11,5 +11,4 @@
GROUP_ID[2]='group2'
GROUP_NUMBER[2]='2.0'
GROUP_TITLE[2]='Logging - CIS only - [group2] **********************************'
GROUP_RUN_BY_DEFAULT[2]='Y' # run it when execute_all is called
GROUP_CHECKS[2]='check21,check22,check23,check24,check25,check26,check27,check28,check29'
-1
View File
@@ -11,5 +11,4 @@
GROUP_ID[3]='group3'
GROUP_NUMBER[3]='3.0'
GROUP_TITLE[3]='Monitoring - CIS only - [group3] *******************************'
GROUP_RUN_BY_DEFAULT[3]='Y' # run it when execute_all is called
GROUP_CHECKS[3]='check31,check32,check33,check34,check35,check36,check37,check38,check39,check310,check311,check312,check313,check314'
-1
View File
@@ -11,5 +11,4 @@
GROUP_ID[4]='group4'
GROUP_NUMBER[4]='4.0'
GROUP_TITLE[4]='Networking - CIS only - [group4] *******************************'
GROUP_RUN_BY_DEFAULT[4]='Y' # run it when execute_all is called
GROUP_CHECKS[4]='check41,check42,check43,check44,check45,check46'
-1
View File
@@ -11,5 +11,4 @@
GROUP_ID[5]='cislevel1'
GROUP_NUMBER[5]='5.0'
GROUP_TITLE[5]='CIS Level 1 - CIS only - [cislevel1] ***************************'
GROUP_RUN_BY_DEFAULT[5]='N' # run it when execute_all is called
GROUP_CHECKS[5]='check11,check12,check13,check14,check15,check16,check17,check18,check19,check110,check111,check112,check113,check115,check116,check117,check118,check119,check120,check122,check21,check23,check24,check25,check26,check31,check32,check33,check34,check35,check38,check312,check313,check314,check41,check42'
-1
View File
@@ -11,5 +11,4 @@
GROUP_ID[6]='cislevel2'
GROUP_NUMBER[6]='6.0'
GROUP_TITLE[6]='CIS Level 2 - CIS only - [cislevel2] ***************************'
GROUP_RUN_BY_DEFAULT[6]='N' # run it when execute_all is called
GROUP_CHECKS[6]='check11,check12,check13,check14,check15,check16,check17,check18,check19,check110,check111,check112,check113,check114,check115,check116,check117,check118,check119,check120,check121,check122,check21,check22,check23,check24,check25,check26,check27,check28,check29,check31,check32,check33,check34,check35,check36,check37,check38,check39,check310,check311,check312,check313,check314,check41,check42,check43,check44'
+1 -2
View File
@@ -14,8 +14,7 @@
GROUP_ID[7]='extras'
GROUP_NUMBER[7]='7.0'
GROUP_TITLE[7]='Extras - all non CIS specific checks - [extras] ****************'
GROUP_RUN_BY_DEFAULT[7]='Y' # run it when execute_all is called
GROUP_CHECKS[7]='extra71,extra72,extra73,extra74,extra75,extra76,extra77,extra78,extra79,extra710,extra711,extra712,extra713,extra714,extra715,extra716,extra717,extra718,extra719,extra720,extra721,extra722,extra723,extra724,extra725,extra726,extra727,extra728,extra729,extra730,extra731,extra732,extra733,extra734,extra735,extra736,extra738,extra739,extra740,extra741,extra742,extra743,extra744,extra745,extra746,extra747,extra748,extra749,extra750,extra751,extra752,extra753,extra754,extra755,extra757,extra758,extra761,extra762,extra763,extra764,extra765,extra767,extra768,extra769,extra770,extra771,extra772,extra773,extra774,extra775,extra776,extra777,extra778,extra779,extra780,extra781,extra782,extra783,extra784,extra785,extra786,extra787,extra788,extra791,extra792,extra793,extra794,extra795,extra796,extra797,extra798,extra799,extra7100,extra7101,extra7102,extra7103,extra7104,extra7105,extra7106,extra7107,extra7108,extra7109,extra7110,extra7111,extra7112,extra7113,extra7114,extra7115,extra7116,extra7117,extra7118,extra7119,extra7120,extra7121,extra7122,extra7123,extra7124,extra7125,extra7126,extra7127,extra7128,extra7129,extra7130,extra7131,extra7132,extra7133,extra7134,extra7135,extra7136,extra7137,extra7138,extra7139,extra7140,extra7141,extra7142,extra7143,extra7144,extra7145,extra7146,extra7147,extra7148,extra7149,extra7150,extra7151,extra7152,extra7153,extra7154,extra7155,extra7156,extra7157,extra7158,extra7159,extra7160,extra7161,extra7162,extra7163,extra7164,extra7165,extra7166,extra7167,extra7168,extra7169,extra7170,extra7171,extra7172,extra7173,extra7174,extra7175,extra7176,extra7177,extra7178,extra7179,extra7180,extra7181,extra7182,extra7183,extra7184,extra7186,extra7187'
GROUP_CHECKS[7]='extra71,extra72,extra73,extra74,extra75,extra76,extra77,extra78,extra79,extra710,extra711,extra712,extra713,extra714,extra715,extra716,extra717,extra718,extra719,extra720,extra721,extra722,extra723,extra724,extra725,extra726,extra727,extra728,extra729,extra730,extra731,extra732,extra733,extra734,extra735,extra736,extra738,extra739,extra740,extra741,extra742,extra743,extra744,extra745,extra746,extra747,extra748,extra749,extra750,extra751,extra752,extra753,extra754,extra755,extra757,extra758,extra761,extra762,extra763,extra764,extra765,extra767,extra768,extra769,extra770,extra771,extra772,extra773,extra774,extra775,extra776,extra777,extra778,extra779,extra780,extra781,extra782,extra783,extra784,extra785,extra786,extra787,extra788,extra791,extra792,extra793,extra794,extra795,extra796,extra797,extra798,extra799,extra7100,extra7101,extra7102,extra7103,extra7104,extra7105,extra7106,extra7107,extra7108,extra7109,extra7110,extra7111,extra7112,extra7113,extra7114,extra7115,extra7116,extra7117,extra7118,extra7119,extra7120,extra7121,extra7122,extra7123,extra7124,extra7125,extra7126,extra7127,extra7128,extra7129,extra7130,extra7131,extra7132,extra7133,extra7134,extra7135,extra7136,extra7137,extra7138,extra7139,extra7140,extra7141,extra7142,extra7143,extra7144,extra7145,extra7146,extra7147,extra7148,extra7149,extra7150,extra7151,extra7152,extra7153,extra7154,extra7155,extra7156,extra7157,extra7158,extra7159,extra7160,extra7161,extra7162,extra7163,extra7164,extra7165,extra7166,extra7167,extra7168,extra7169,extra7170,extra7171,extra7172,extra7173,extra7174,extra7175,extra7176,extra7177,extra7178,extra7179,extra7180,extra7181,extra7182,extra7183,extra7184,extra7185,extra7186,extra7187'
# Extras 759 and 760 (lambda variables and code secrets finder are not included)
# to run detect-secrets use `./prowler -g secrets`
-1
View File
@@ -14,5 +14,4 @@
GROUP_ID[8]='forensics-ready'
GROUP_NUMBER[8]='8.0'
GROUP_TITLE[8]='Forensics Readiness - [forensics-ready] ************************'
GROUP_RUN_BY_DEFAULT[8]='N' # run it when execute_all is called
GROUP_CHECKS[8]='check21,check22,check23,check24,check25,check26,check27,check29,extra712,extra713,extra714,extra715,extra717,extra718,extra719,extra720,extra721,extra722,extra725,extra7101,extra794'
-1
View File
@@ -14,7 +14,6 @@
GROUP_ID[9]='gdpr'
GROUP_NUMBER[9]='9.0'
GROUP_TITLE[9]='GDPR Readiness - ONLY AS REFERENCE - [gdpr] ********************'
GROUP_RUN_BY_DEFAULT[9]='N' # run it when execute_all is called
GROUP_CHECKS[9]='extra718,extra725,extra727,check12,check113,check114,extra71,extra731,extra732,extra733,check25,check39,check21,check22,check23,check24,check26,check27,check35,extra726,extra714,extra715,extra717,extra719,extra720,extra721,extra722,check43,check25,extra714,extra729,extra734,extra735,extra736,extra738,extra740,extra761,check11,check110,check111,check112,check116,check120,check122,check13,check14,check15,check16,check17,check18,check19,check28,check29,check31,check310,check311,check312,check313,check314,check32,check33,check34,check36,check37,check38,check41,check42,extra711,extra72,extra723,extra730,extra739,extra76,extra763,extra778,extra78,extra792,extra798'
# Resources:
-1
View File
@@ -14,5 +14,4 @@
GROUP_ID[9]='my-custom-group'
GROUP_NUMBER[9]='9.0'
GROUP_TITLE[9]='My Custom Group - [my-custom-group] ****************************'
GROUP_RUN_BY_DEFAULT[9]='N' # run it when execute_all is called
GROUP_CHECKS[9]='checkNN,checkMM'
+55 -38
View File
@@ -16,43 +16,60 @@
# check environment variables and if not, it checks and loads credentials from
# instance profile (metadata server) if runs in an EC2 instance
INSTANCE_PROFILE=$(curl -s -m 1 http://169.254.169.254/latest/meta-data/iam/security-credentials/)
if echo "$INSTANCE_PROFILE" | grep -q '404 - Not Found'; then
INSTANCE_PROFILE=
fi
aws_profile_loader() {
INSTANCE_PROFILE=$(curl -s -m 1 http://169.254.169.254/latest/meta-data/iam/security-credentials/)
if echo "$INSTANCE_PROFILE" | grep -q '404 - Not Found'; then
INSTANCE_PROFILE=
fi
if [[ $PROFILE ]]; then
PROFILE_OPT="--profile $PROFILE"
elif [[ $AWS_ACCESS_KEY_ID && $AWS_SECRET_ACCESS_KEY || $AWS_SESSION_TOKEN || $AWS_PROFILE ]];then
PROFILE="$AWS_PROFILE"
PROFILE_OPT=""
elif [[ -n $AWS_CONTAINER_CREDENTIALS_RELATIVE_URI ]] && [[ -z $INSTANCE_PROFILE ]]; then
PROFILE="INSTANCE-PROFILE"
AWS_ACCESS_KEY_ID=$(curl -s 169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI | grep AccessKeyId | cut -d':' -f2 | sed 's/[^0-9A-Z]*//g')
AWS_SECRET_ACCESS_KEY_ID=$(curl -s 169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI | grep SecretAccessKey | cut -d':' -f2 | sed 's/[^0-9A-Za-z/+=]*//g')
AWS_SESSION_TOKEN=$(curl -s 169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI grep Token| cut -d':' -f2 | sed 's/[^0-9A-Za-z/+=]*//g')
elif [[ $AWS_WEB_IDENTITY_TOKEN_FILE ]]; then
PROFILE=""
PROFILE_OPT=""
elif [[ $INSTANCE_PROFILE ]]; then
PROFILE="INSTANCE-PROFILE"
AWS_ACCESS_KEY_ID=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/${INSTANCE_PROFILE} | grep AccessKeyId | cut -d':' -f2 | sed 's/[^0-9A-Z]*//g')
AWS_SECRET_ACCESS_KEY_ID=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/${INSTANCE_PROFILE} | grep SecretAccessKey | cut -d':' -f2 | sed 's/[^0-9A-Za-z/+=]*//g')
AWS_SESSION_TOKEN=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/${INSTANCE_PROFILE} grep Token| cut -d':' -f2 | sed 's/[^0-9A-Za-z/+=]*//g')
elif [[ $AWS_EXECUTION_ENV == "CloudShell" ]]; then
PROFILE_OPT=""
else
PROFILE="default"
if [[ $PROFILE ]]; then
PROFILE_OPT="--profile $PROFILE"
fi
# Backing up $PROFILE_OPT needed to renew assume_role
PROFILE_OPT_BAK=$PROFILE_OPT
# Set default region by aws config, fall back to us-east-1
REGION_CONFIG=$(aws configure get region)
if [[ $REGION_OPT ]]; then
REGION="$REGION_OPT"
elif [[ $REGION_CONFIG ]]; then
REGION="$REGION_CONFIG"
else
REGION="us-east-1"
fi
elif [[ $AWS_ACCESS_KEY_ID && $AWS_SECRET_ACCESS_KEY || $AWS_SESSION_TOKEN || $AWS_PROFILE ]];then
PROFILE="$AWS_PROFILE"
PROFILE_OPT=""
elif [[ -n $AWS_CONTAINER_CREDENTIALS_RELATIVE_URI ]] && [[ -z $INSTANCE_PROFILE ]]; then
PROFILE="INSTANCE-PROFILE"
AWS_ACCESS_KEY_ID=$(curl -s 169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI | grep AccessKeyId | cut -d':' -f2 | sed 's/[^0-9A-Z]*//g')
AWS_SECRET_ACCESS_KEY_ID=$(curl -s 169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI | grep SecretAccessKey | cut -d':' -f2 | sed 's/[^0-9A-Za-z/+=]*//g')
AWS_SESSION_TOKEN=$(curl -s 169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI grep Token| cut -d':' -f2 | sed 's/[^0-9A-Za-z/+=]*//g')
elif [[ $AWS_WEB_IDENTITY_TOKEN_FILE ]]; then
PROFILE=""
PROFILE_OPT=""
elif [[ $INSTANCE_PROFILE ]]; then
PROFILE="INSTANCE-PROFILE"
AWS_ACCESS_KEY_ID=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/${INSTANCE_PROFILE} | grep AccessKeyId | cut -d':' -f2 | sed 's/[^0-9A-Z]*//g')
AWS_SECRET_ACCESS_KEY_ID=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/${INSTANCE_PROFILE} | grep SecretAccessKey | cut -d':' -f2 | sed 's/[^0-9A-Za-z/+=]*//g')
AWS_SESSION_TOKEN=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/${INSTANCE_PROFILE} grep Token| cut -d':' -f2 | sed 's/[^0-9A-Za-z/+=]*//g')
elif [[ $AWS_EXECUTION_ENV == "CloudShell" ]]; then
PROFILE_OPT=""
else
PROFILE="default"
PROFILE_OPT="--profile $PROFILE"
fi
# Backing up $PROFILE_OPT needed to renew assume_role
PROFILE_OPT_BAK=$PROFILE_OPT
# Set default region by aws config, fall back to us-east-1
REGION_CONFIG=$(aws configure get region)
if [[ $REGION_OPT ]]; then
REGION="$REGION_OPT"
elif [[ $REGION_CONFIG ]]; then
REGION="$REGION_CONFIG"
else
REGION="us-east-1"
fi
}
# Function to get all regions
get_regions() {
# Get list of regions based on include/whoami
if ! REGIONS=$($AWSCLI ec2 describe-regions \
--query 'Regions[].RegionName' \
--output text ${PROFILE_OPT} \
--region "${REGION_FOR_STS}" \
--region-names ${FILTERREGION//[,]/ } 2>&1)
then
echo "$OPTRED Access Denied trying to describe regions! Review permissions as described here: https://github.com/prowler-cloud/prowler/#requirements-and-installation $OPTNORMAL"
EXITCODE=1
exit $EXITCODE
fi
}
+22 -9
View File
@@ -12,12 +12,25 @@
# specific language governing permissions and limitations under the License.
# AWS-CLI detector variable
if [ ! -z $(which aws) ]; then
AWSCLI=$(which aws)
elif [ ! -z $(type -p aws) ]; then
AWSCLI=$(type -p aws)
else
echo -e "\n$RED ERROR!$NORMAL AWS-CLI (aws command) not found. Make sure it is installed correctly and in your \$PATH\n"
EXITCODE=1
exit $EXITCODE
fi
aws_cli_detector() {
if [ ! -z $(which aws) ]; then
AWSCLI=$(which aws)
elif [ ! -z $(type -p aws) ]; then
AWSCLI=$(type -p aws)
else
echo -e "\n$RED ERROR!$NORMAL AWS-CLI (aws command) not found. Make sure it is installed correctly and in your \$PATH\n"
EXITCODE=1
exit $EXITCODE
fi
}
set_aws_default_output() {
# Ensures command output will always be set to JSON.
# If the default value is already set, ORIGINAL_OUTPUT will be used to store it and reset it at cleanup
if [[ -z "${AWS_DEFAULT_OUTPUT}" ]]; then
ORIGINAL_OUTPUT=$AWS_DEFAULT_OUTPUT
export AWS_DEFAULT_OUTPUT="json"
else
export AWS_DEFAULT_OUTPUT="json"
fi
}
+1 -1
View File
@@ -19,7 +19,7 @@ prowlerBanner() {
echo -e " | |_) | | | (_) \ V V /| | __/ |"
echo -e " | .__/|_| \___/ \_/\_/ |_|\___|_|v$PROWLER_VERSION"
echo -e " |_|$NORMAL$BLUE the handy cloud security tool$NORMAL\n"
echo -e "$YELLOW Date: $(date)"
echo -e "$YELLOW Date: $(date)$NORMAL"
printColorsCode
fi
}
+30 -13
View File
@@ -11,23 +11,40 @@
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
# Colors
# NOTE: Your editor may NOT show the 0x1b / escape character left of the '['
NORMAL=""
WARNING="" # Warning (brown)
NOTICE="" # Notice (yellow)
OK="" # Ok (green)
BAD="" # Bad (red)
CYAN=""
BLUE=""
BROWN=""
MAGENTA=""
RED=""
YELLOW=""
set_colors() {
if [[ "${MODE}" =~ "mono" ]]
then
NORMAL=""
WARNING=""
NOTICE=""
OK=""
BAD=""
CYAN=""
BLUE=""
BROWN=""
MAGENTA=""
RED=""
YELLOW=""
else
NORMAL=""
WARNING="" # Warning (brown)
NOTICE="" # Notice (yellow)
OK="" # Ok (green)
BAD="" # Bad (red)
CYAN=""
BLUE=""
BROWN=""
MAGENTA=""
RED=""
YELLOW=""
fi
}
printColorsCode(){
if [[ $MONOCHROME -eq 0 ]]; then
if [[ ! "$MODE" =~ "mono" ]]
then
echo -e "\n$NORMAL Color code for results: "
echo -e " - $NOTICE INFO (Information)$NORMAL"
echo -e " - $OK PASS (Recommended value)$NORMAL"
-11
View File
@@ -37,14 +37,3 @@ saveReport(){
textInfo "IAM Credential Report saved in $TEMP_REPORT_FILE"
fi
}
# Delete temporary report file
cleanTemp(){
if [[ $KEEPCREDREPORT -ne 1 ]]; then
rm -fr $TEMP_REPORT_FILE
fi
cleanSTSAssumeFile
}
# Delete the temporary report file if we get interrupted/terminated
trap cleanTemp EXIT
+16 -16
View File
@@ -14,39 +14,39 @@
custom_checks(){
# check if the path is an S3 URI
if grep -q -E "^s3://([^/]+)/?(.*?([^/]+)/?)?$" <<< "$EXTERNAL_CHECKS_PATH"; then
if grep -q "check*" <<< "$("${AWSCLI}" s3 ls "${EXTERNAL_CHECKS_PATH}" $PROFILE_OPT)"; then
if grep -q "check*" <<< "$("${AWSCLI}" s3 ls "${EXTERNAL_CHECKS_PATH}" "${PROFILE_OPT}")"; then
# download s3 object
echo -e "$NOTICE Downloading custom checks from S3 URI $EXTERNAL_CHECKS_PATH...$NORMAL"
S3_CHECKS_TEMP_FOLDER="$PROWLER_DIR/s3-custom-checks"
echo -e "${NOTICE} Downloading custom checks from S3 URI ${EXTERNAL_CHECKS_PATH}...${NORMAL}"
S3_CHECKS_TEMP_FOLDER="${PROWLER_DIR}/s3-custom-checks"
mkdir "${S3_CHECKS_TEMP_FOLDER}"
$AWSCLI s3 sync "$EXTERNAL_CHECKS_PATH" "${S3_CHECKS_TEMP_FOLDER}" $PROFILE_OPT > /dev/null
${AWSCLI} s3 sync "${EXTERNAL_CHECKS_PATH}" "${S3_CHECKS_TEMP_FOLDER}" "${PROFILE_OPT}" > /dev/null
# verify if there are checks
for checks in "${S3_CHECKS_TEMP_FOLDER}"/check*; do
. "$checks"
echo -e "$OK Check $(basename "$checks") was included!$NORMAL"
for CHECKS in "${S3_CHECKS_TEMP_FOLDER}"/check*; do
. "${CHECKS}"
echo -e "${OK} Check $(basename "${CHECKS}") was included!${NORMAL}"
done
echo -e "$OK Success! Custom checks were downloaded and included, starting Prowler...$NORMAL"
echo -e "${OK} Success! Custom checks were downloaded and included, starting Prowler...${NORMAL}"
# remove temporary dir
rm -rf "${S3_CHECKS_TEMP_FOLDER}"
else
echo "$BAD FAIL! Access Denied trying to download custom checks or $EXTERNAL_CHECKS_PATH does not contain any checks, please make sure it is correct and/or you have permissions to get the S3 objects.$NORMAL"
echo "${BAD} FAIL! Access Denied trying to download custom checks or ${EXTERNAL_CHECKS_PATH} does not contain any checks, please make sure it is correct and/or you have permissions to get the S3 objects.${NORMAL}"
EXITCODE=1
# remove temporary dir
rm -rf "${S3_CHECKS_TEMP_FOLDER}"
exit $EXITCODE
exit "${EXITCODE}"
fi
else
# verify if input directory exists with checks
if ls "${EXTERNAL_CHECKS_PATH}"/check* > /dev/null 2>&1; then
for checks in "${EXTERNAL_CHECKS_PATH}"/check*; do
. "$checks"
echo -e "$OK Check $(basename "$checks") was included!$NORMAL"
for CHECKS in "${EXTERNAL_CHECKS_PATH}"/check*; do
. "${CHECKS}"
echo -e "${OK} Check $(basename "${CHECKS}") was included!${NORMAL}"
done
echo -e "$OK Success! Custom checks were included, starting Prowler...$NORMAL"
echo -e "${OK} Success! Custom checks were included, starting Prowler...${NORMAL}"
else
echo "$BAD FAIL! $EXTERNAL_CHECKS_PATH does not exist or not contain checks, please input a valid custom checks path.$NORMAL"
echo "${BAD} FAIL! ${EXTERNAL_CHECKS_PATH} does not exist or not contain checks, please input a valid custom checks path.${NORMAL}"
EXITCODE=1
exit $EXITCODE
exit "${EXITCODE}"
fi
fi
}
@@ -11,10 +11,30 @@
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
# AWS-CLI detector variable
JQ=$(which jq)
if [ -z "${JQ}" ]; then
echo -e "\n$RED ERROR!$NORMAL jq not found. Make sure it is installed correctly and in your \$PATH\n"
EXITCODE=1
exit $EXITCODE
fi
# Output Colors
OPTRED=""
OPTNORMAL=""
# AWS Region
REGION=""
FILTERREGION=""
# Max AWS CLI items
MAXITEMS=100
# Default output mode
MODE="text"
# Security Hub
SEND_TO_SECURITY_HUB=0
# Date & Time
PROWLER_START_TIME=$( date -u +"%Y-%m-%dT%H:%M:%S%z" )
OUTPUT_DATE=$(date -u +"%Y%m%d%H%M%S")
# Default Prowler Options
QUIET=0
SEP=','
KEEPCREDREPORT=0
EXITCODE=0
FAILED_CHECK_FAILED_SCAN=1
+131
View File
@@ -0,0 +1,131 @@
#!/usr/bin/env bash
# Copyright 2018 Toni de la Fuente
# Prowler is a tool that provides automate auditing and hardening guidance of an
# AWS account. It is based on AWS-CLI commands. It follows some guidelines
# present in the CIS Amazon Web Services Foundations Benchmark at:
# https://d0.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
# Contact the author at https://blyx.com/contact
# and open issues or ask questions at https://github.com/prowler-cloud/prowler
# Code is licensed as Apache License 2.0 as specified in
# each file. You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Function to execute the check
execute_check() {
if [[ -n "${ACCOUNT_TO_ASSUME}" || -n "${ROLE_TO_ASSUME}" ]]; then
# echo ******* I am here again to check on my role *******
# Following logic looks for time remaining in the session and review it
# if it is less than 600 seconds, 10 minutes.
CURRENT_TIMESTAMP=$(date -u "+%s")
SESSION_TIME_REMAINING=$(expr $AWS_SESSION_EXPIRATION - $CURRENT_TIMESTAMP)
# echo SESSION TIME REMAINING IN SECONDS: $SESSION_TIME_REMAINING
MINIMUM_REMAINING_TIME_ALLOWED=600
if (( $MINIMUM_REMAINING_TIME_ALLOWED > $SESSION_TIME_REMAINING )); then
# echo LESS THAN 10 MIN LEFT: RE-ASSUMING...
unset AWS_ACCESS_KEY_ID
unset AWS_SECRET_ACCESS_KEY
unset AWS_SESSION_TOKEN
assume_role
fi
fi
CHECK_ID="$1"
# See if this is an alternate name for a check
# for example, we might have been passed 1.01 which is another name for 1.1
local alternate_name_var=CHECK_ALTERNATE_$1
local alternate_name=${!alternate_name_var}
# See if this check defines an ASFF Type, if so, use this, falling back to a sane default
# For a list of Types Taxonomy, see: https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-findings-format-type-taxonomy.html
local asff_type_var=CHECK_ASFF_TYPE_$1
CHECK_ASFF_TYPE="${!asff_type_var:-Software and Configuration Checks}"
local asff_compliance_type_var=CHECK_ASFF_COMPLIANCE_TYPE_$1
CHECK_ASFF_COMPLIANCE_TYPE="${!asff_compliance_type_var:-Software and Configuration Checks}"
# See if this check defines an ASFF Resource Type, if so, use this, falling back to a sane default
# For a list of Resource Types, see: https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-findings-format.html#asff-resources
local asff_resource_type_var=CHECK_ASFF_RESOURCE_TYPE_$1
CHECK_ASFF_RESOURCE_TYPE="${!asff_resource_type_var:-AwsAccount}"
local severity_var=CHECK_SEVERITY_$1
CHECK_SEVERITY="${!severity_var}"
local servicename_var=CHECK_SERVICENAME_$1
CHECK_SERVICENAME="${!servicename_var}"
local risk_var=CHECK_RISK_$1
CHECK_RISK="${!risk_var}"
local remediation_var=CHECK_REMEDIATION_$1
CHECK_REMEDIATION="${!remediation_var}"
local doc_var=CHECK_DOC_$1
CHECK_DOC="${!doc_var}"
local caf_epic_var=CHECK_CAF_EPIC_$1
CHECK_CAF_EPIC="${!caf_epic_var}"
SECURITYHUB_NEW_FINDINGS_IDS=()
# Generate the credential report, only if it is group1 related which checks we
# run so that the checks can safely assume it's available
# set the custom ignores list for this check
ignores="$(awk "/${1}/{print}" <(echo "${ALLOWLIST}"))"
if [ ${alternate_name} ];then
if [[ ${alternate_name} == check1* || ${alternate_name} == extra71 || ${alternate_name} == extra774 || ${alternate_name} == extra7123 ]];then
if [ ! -s $TEMP_REPORT_FILE ];then
genCredReport
saveReport
fi
fi
show_check_title ${alternate_name}
if is_junit_output_enabled; then
prepare_junit_check_output "$1"
fi
# Execute the check
IGNORES="${ignores}" CHECK_NAME="$1" ${alternate_name}
if is_junit_output_enabled; then
finalise_junit_check_output "$1"
fi
if [[ "$SEND_TO_SECURITY_HUB" -eq 1 ]]; then
resolveSecurityHubPreviousFails "$1"
fi
else
# Check to see if this is a real check
local check_id_var=CHECK_ID_$1
local check_id=${!check_id_var}
if [ ${check_id} ]; then
if [[ ${check_id} == 1* || ${check_id} == 7.1 || ${check_id} == 7.74 || ${check_id} == 7.123 ]];then
if [ ! -s $TEMP_REPORT_FILE ];then
genCredReport
saveReport
fi
fi
show_check_title "$1"
if is_junit_output_enabled; then
prepare_junit_check_output "$1"
fi
# Execute the check
IGNORES="${ignores}" CHECK_NAME="$1" $1
if is_junit_output_enabled; then
finalise_junit_check_output "$1"
fi
if [[ "$SEND_TO_SECURITY_HUB" -eq 1 ]]; then
resolveSecurityHubPreviousFails "$1"
fi
else
textFail "Check ${CHECK_ID} does not exist. Use a valid check name (i.e. check41 or extra71)";
exit $EXITCODE
fi
fi
}
+1 -1
View File
@@ -21,7 +21,7 @@ JUNIT_SKIPPED_COUNT="0"
JUNIT_ERRORS_COUNT="0"
is_junit_output_enabled() {
if [[ " ${MODES[@]} " =~ " junit-xml " ]]; then
if [[ " ${MODE} " =~ "junit-xml" ]]; then
true
else
false
+137
View File
@@ -0,0 +1,137 @@
#!/usr/bin/env bash
# Copyright 2018 Toni de la Fuente
# Prowler is a tool that provides automate auditing and hardening guidance of an
# AWS account. It is based on AWS-CLI commands. It follows some guidelines
# present in the CIS Amazon Web Services Foundations Benchmark at:
# https://d0.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
# Contact the author at https://blyx.com/contact
# and open issues or ask questions at https://github.com/prowler-cloud/prowler
# Code is licensed as Apache License 2.0 as specified in
# each file. You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Load all of the groups of checks inside groups folder named as "groupNumber*"
load_groups() {
for group in "${PROWLER_DIR}"/groups/group[0-9]*; do
# shellcheck source=/dev/null
. "${group}"
done
}
# Load a single check
load_check(){
CHECK=${1}
# shellcheck source=/dev/null
. "$PROWLER_DIR/checks/${CHECK}"
}
get_checks() {
# Parses the check file into CHECK_ID's.
if [[ -n "$CHECK_FILE" ]]
then
if [[ -f $CHECK_FILE ]]
then
# Parses the file, converting it to a comma seperated list. Ignores all # comments and removes extra blank spaces
# REVIEW THIS OPTION -C
CHECKS_FROM_FILE="$(awk '!/^[[:space:]]*#/{print }' <(cat ${CHECK_FILE} | sed 's/[[:space:]]*#.*$//g;/^$/d' | sed 'H;1h;$!d;x;y/\n/,/' | tr -d ' '))"
IFS=',' read -ra TOTAL_CHECKS <<< "${CHECKS_FROM_FILE}"
else
# If the file doesn't exist, exits Prowler
echo "$CHECK_FILE does not exist"
EXITCODE=1
exit $EXITCODE
fi
# If '-g <group_id>' has been specified, only show the titles of checks within the specified group
# reading groups from GROUP_ID_READ
elif [[ "${GROUP_ID_READ}" ]]
then
for GROUP in "${GROUP_ID[@]}"
do
if [[ "${GROUP}" == "${GROUP_ID_READ}" ]]
then
IS_GROUP=1
fi
done
if [[ IS_GROUP -eq 0 ]]
then
textFail "Group ${GROUP_ID_READ} does not exist. Valid check groups are: ${GROUP_ID[*]}"
exit $EXITCODE
fi
# Iterate over every group removing echar comma
for GROUP_IDENTIFIER in ${GROUP_ID_READ//,/ }
do
# Iterate over every GroupID to find the belongin checks
for I in "${!GROUP_ID[@]}"
do
if [[ "${EXTRAS}" -eq 1 && "${GROUP_ID[I]}" == "extras" ]]
then
continue
else
if [[ "${GROUP_ID[I]}" == "${GROUP_IDENTIFIER}" ]]
then
# shellcheck disable=SC2068
for CHECK_IDENTIFIER in ${GROUP_CHECKS[I]//,/ }
do
# Include every check if not present
if [[ ! "${CHECK_LIST_BY_GROUP[*]}" =~ ${CHECK_IDENTIFIER} ]] && ! grep -E -w -q "${EXCLUDE_CHECK_ID//,/|}" <<< "${CHECK_IDENTIFIER}"
then
CHECK_LIST_BY_GROUP+=("${CHECK_IDENTIFIER}")
fi
done
fi
fi
done
done
TOTAL_CHECKS=("${CHECK_LIST_BY_GROUP[@]}")
# If -c option is set with checks
elif [ -n "${CHECK_ID}" ]
then
IFS=',' read -ra TOTAL_CHECKS <<< "${CHECK_ID}"
# If no option input to include/exclude checks
else
# if -e is passed we dont want extra checks
if [[ "${EXTRAS}" -eq 1 ]]
then
if [[ -n "$EXCLUDE_CHECK_ID" ]]
then
EXCLUDE_CHECK_ID="${EXCLUDE_CHECK_ID},${GROUP_CHECKS[7]}"
else
EXCLUDE_CHECK_ID="${GROUP_CHECKS[7]}"
fi
fi
# Always ignore the following checks if not supplied with -C
IGNORED_CHECKS="sample,extra9999"
EXCLUDE_CHECK_ID="${EXCLUDE_CHECK_ID/%/,}${IGNORED_CHECKS}"
for CHECK in "${PROWLER_DIR}"/checks/check*; do
# Relative path required to load every check
CHECK_DIR_NAME=$(basename "${CHECK}")
# Name of the check
CHECK_NAME=${CHECK_DIR_NAME//check_/}
# If a group identifier is passed
if ! grep -E -w -q "${EXCLUDE_CHECK_ID//,/|}" <<< "${CHECK_NAME}"
then
TOTAL_CHECKS+=("${CHECK_NAME}")
fi
done
fi
# Iterate over the final list of checks after being parsed all the input options to load the selected checks
for LOAD_PATH_CHECK in "${TOTAL_CHECKS[@]}"
do
# If the check is extra, the path needs to add check_ after the check name
if [[ "${LOAD_PATH_CHECK}" =~ 'extra' ]]
then
LOAD_PATH_CHECK=${LOAD_PATH_CHECK/#/check_}
fi
load_check "${LOAD_PATH_CHECK}"
done
}
+123 -121
View File
@@ -145,65 +145,21 @@ bsd_replace_sed(){
sed -i '' $1 $2
}
# Functions to manage dates depending on OS
if [ "$OSTYPE" == "linux-gnu" ] || [ "$OSTYPE" == "linux-musl" ]; then
TEMP_REPORT_FILE=$(mktemp -t -p /tmp prowler.cred_report-XXXXXX)
# function to compare in days, usage how_older_from_today date
# date format %Y-%m-%d
how_older_from_today() {
gnu_how_older_from_today "$1"
}
timestamp_to_date() {
gnu_timestamp_to_date "$1"
}
decode_report() {
gnu_decode_report
}
how_many_days_from_today() {
gnu_how_many_days_from_today "$1"
}
get_date_previous_than_months() {
gnu_get_date_previous_than_months "$1"
}
get_time_in_milliseconds() {
gnu_get_time_in_milliseconds
}
get_iso8601_timestamp() {
gnu_get_iso8601_timestamp
}
test_tcp_connectivity() {
gnu_test_tcp_connectivity "$1" "$2" "$3"
}
convert_date_to_timestamp() {
gnu_convert_date_to_timestamp "$1"
}
replace_sed() {
gnu_replace_sed $1 $2
}
elif [[ "$OSTYPE" == "darwin"* ]] || [[ "$OSTYPE" == "freebsd"* ]]; then
# BSD/OSX commands compatibility
TEMP_REPORT_FILE=$(mktemp -t prowler.cred_report-XXXXXX)
# It is possible that the user has installed GNU coreutils on OS X. By default, this will make GNU commands
# available with a 'g' prefix, e.g. 'gdate'. Test if this is present, and use it if so, as it supports more features.
# The user also may have replaced the default Mac OS X BSD tools with the GNU coreutils equivalents.
# Only GNU date/base64 allows --version as a valid argument, so use the validity of this argument
# as a means to detect that coreutils is installed and is overriding the default tools
GDATE=$(which gdate)
if [ -n "${GDATE}" ]; then
DATE_CMD="gdate"
fi
GBASE64=$(which gbase64)
if [ -n "${GBASE64}" ]; then
BASE64_CMD="gbase64"
fi
if "$DATE_CMD" --version >/dev/null 2>&1 ; then
os_detector() {
# Functions to manage dates depending on OS
if [ "$OSTYPE" == "linux-gnu" ] || [ "$OSTYPE" == "linux-musl" ]; then
TEMP_REPORT_FILE=$(mktemp -t -p /tmp prowler.cred_report-XXXXXX)
# function to compare in days, usage how_older_from_today date
# date format %Y-%m-%d
how_older_from_today() {
gnu_how_older_from_today "$1"
}
timestamp_to_date() {
gnu_timestamp_to_date "$1"
}
decode_report() {
gnu_decode_report
}
how_many_days_from_today() {
gnu_how_many_days_from_today "$1"
}
@@ -216,83 +172,129 @@ elif [[ "$OSTYPE" == "darwin"* ]] || [[ "$OSTYPE" == "freebsd"* ]]; then
get_iso8601_timestamp() {
gnu_get_iso8601_timestamp
}
test_tcp_connectivity() {
gnu_test_tcp_connectivity "$1" "$2" "$3"
}
convert_date_to_timestamp() {
gnu_convert_date_to_timestamp "$1"
}
else
replace_sed() {
gnu_replace_sed $1 $2
}
elif [[ "$OSTYPE" == "darwin"* ]] || [[ "$OSTYPE" == "freebsd"* ]]; then
# BSD/OSX commands compatibility
TEMP_REPORT_FILE=$(mktemp -t prowler.cred_report-XXXXXX)
# It is possible that the user has installed GNU coreutils on OS X. By default, this will make GNU commands
# available with a 'g' prefix, e.g. 'gdate'. Test if this is present, and use it if so, as it supports more features.
# The user also may have replaced the default Mac OS X BSD tools with the GNU coreutils equivalents.
# Only GNU date/base64 allows --version as a valid argument, so use the validity of this argument
# as a means to detect that coreutils is installed and is overriding the default tools
GDATE=$(which gdate)
if [ -n "${GDATE}" ]; then
DATE_CMD="gdate"
fi
GBASE64=$(which gbase64)
if [ -n "${GBASE64}" ]; then
BASE64_CMD="gbase64"
fi
if "$DATE_CMD" --version >/dev/null 2>&1 ; then
how_older_from_today() {
gnu_how_older_from_today "$1"
}
timestamp_to_date() {
gnu_timestamp_to_date "$1"
}
how_many_days_from_today() {
gnu_how_many_days_from_today "$1"
}
get_date_previous_than_months() {
gnu_get_date_previous_than_months "$1"
}
get_time_in_milliseconds() {
gnu_get_time_in_milliseconds
}
get_iso8601_timestamp() {
gnu_get_iso8601_timestamp
}
convert_date_to_timestamp() {
gnu_convert_date_to_timestamp "$1"
}
else
how_older_from_today() {
bsd_how_older_from_today "$1"
}
timestamp_to_date() {
bsd_timestamp_to_date "$1"
}
how_many_days_from_today() {
bsd_how_many_days_from_today "$1"
}
get_date_previous_than_months() {
bsd_get_date_previous_than_months "$1"
}
get_time_in_milliseconds() {
bsd_get_time_in_milliseconds
}
get_iso8601_timestamp() {
bsd_get_iso8601_timestamp
}
convert_date_to_timestamp() {
bsd_convert_date_to_timestamp "$1"
}
fi
if "$BASE64_CMD" --version >/dev/null 2>&1 ; then
decode_report() {
gnu_decode_report
}
else
decode_report() {
bsd_decode_report
}
fi
test_tcp_connectivity() {
bsd_test_tcp_connectivity "$1" "$2" "$3"
}
replace_sed() {
bsd_replace_sed $1 $2
}
elif [[ "$OSTYPE" == "cygwin" ]]; then
# POSIX compatibility layer and Linux environment emulation for Windows
TEMP_REPORT_FILE=$(mktemp -t -p /tmp prowler.cred_report-XXXXXX)
how_older_from_today() {
bsd_how_older_from_today "$1"
gnu_how_older_from_today "$1"
}
timestamp_to_date() {
bsd_timestamp_to_date "$1"
gnu_timestamp_to_date "$1"
}
how_many_days_from_today() {
bsd_how_many_days_from_today "$1"
}
get_date_previous_than_months() {
bsd_get_date_previous_than_months "$1"
}
get_time_in_milliseconds() {
bsd_get_time_in_milliseconds
}
get_iso8601_timestamp() {
bsd_get_iso8601_timestamp
}
convert_date_to_timestamp() {
bsd_convert_date_to_timestamp "$1"
}
fi
if "$BASE64_CMD" --version >/dev/null 2>&1 ; then
decode_report() {
gnu_decode_report
}
else
decode_report() {
bsd_decode_report
how_many_days_from_today() {
gnu_how_many_days_from_today "$1"
}
get_date_previous_than_months() {
gnu_get_date_previous_than_months "$1"
}
get_time_in_milliseconds() {
gnu_get_time_in_milliseconds
}
get_iso8601_timestamp() {
gnu_get_iso8601_timestamp
}
test_tcp_connectivity() {
gnu_test_tcp_connectivity "$1" "$2" "$3"
}
convert_date_to_timestamp() {
gnu_convert_date_to_timestamp "$1"
}
replace_sed() {
gnu_replace_sed $1 $2
}
else
echo "Unknown Operating System! Valid \$OSTYPE: linux-gnu, linux-musl, darwin* or cygwin"
echo "Found: $OSTYPE"
EXITCODE=1
exit $EXITCODE
fi
test_tcp_connectivity() {
bsd_test_tcp_connectivity "$1" "$2" "$3"
}
replace_sed() {
bsd_replace_sed $1 $2
}
elif [[ "$OSTYPE" == "cygwin" ]]; then
# POSIX compatibility layer and Linux environment emulation for Windows
TEMP_REPORT_FILE=$(mktemp -t -p /tmp prowler.cred_report-XXXXXX)
how_older_from_today() {
gnu_how_older_from_today "$1"
}
timestamp_to_date() {
gnu_timestamp_to_date "$1"
}
decode_report() {
gnu_decode_report
}
how_many_days_from_today() {
gnu_how_many_days_from_today "$1"
}
get_date_previous_than_months() {
gnu_get_date_previous_than_months "$1"
}
get_time_in_milliseconds() {
gnu_get_time_in_milliseconds
}
get_iso8601_timestamp() {
gnu_get_iso8601_timestamp
}
test_tcp_connectivity() {
gnu_test_tcp_connectivity "$1" "$2" "$3"
}
convert_date_to_timestamp() {
gnu_convert_date_to_timestamp "$1"
}
replace_sed() {
gnu_replace_sed $1 $2
}
else
echo "Unknown Operating System! Valid \$OSTYPE: linux-gnu, linux-musl, darwin* or cygwin"
echo "Found: $OSTYPE"
EXITCODE=1
exit $EXITCODE
fi
}
+43 -25
View File
@@ -45,7 +45,6 @@ EXTENSION_TEXT="txt"
EXTENSION_HTML="html"
HTML_LOGO_URL="https://github.com/prowler-cloud/prowler/"
HTML_LOGO_IMG="https://github.com/prowler-cloud/prowler/raw/master/util/html/prowler-logo-new.png"
TIMESTAMP=$(get_iso8601_timestamp)
PROWLER_PARAMETERS=$@
@@ -65,19 +64,29 @@ set_exitcode () {
# Add header to certain output files readed from mode
output_files_init() {
for MODE in "${MODES[@]}";
OIFS="${IFS}"
IFS=','
for MODE_TYPE in ${MODE}
do
if [ "${MODE}" == 'html' ]
if [ "${MODE_TYPE}" == 'html' ]
then
addHtmlHeader
HTML_REPORT_INIT="1"
export HTML_REPORT_INIT
fi
if [ "${MODE}" == 'csv' ]
if [ "${MODE_TYPE}" == 'csv' ]
then
printCsvHeader
fi
done
IFS="${OIFS}"
}
# Close HTML output file
output_files_end() {
if [[ "${MODE}" =~ "html" ]]; then
addHtmlFooter >> "${OUTPUT_FILE_NAME}.${EXTENSION_HTML}"
fi
}
# Check if resource checked is allowlisted
@@ -91,7 +100,7 @@ allowlist_check() {
# IGNORE_CHECK_NAME is the check with resources allowlisted
# RESOURCE_VALUE is what it comes after 'CHECK_NAME:'
IGNORE_CHECK_NAME=$(awk -F ":" '{print $1}' <<< "${excluded_item}")
RESOURCE_VALUE=$(awk -F "${CHECK_NAME}:" '{print $2}' <<< "${excluded_item}")
RESOURCE_VALUE=$(awk -F "${CHECK_NAME}:" '{print $2}' <<< "${excluded_item}")
if [[ "${IGNORE_CHECK_NAME}" == "${CHECK_NAME}" ]]
then
if [[ "${CHECKED_VALUE}" =~ ${RESOURCE_VALUE} ]]
@@ -100,7 +109,7 @@ allowlist_check() {
break
fi
fi
done <<< "$IGNORES"
echo "${CHECK_RESULT}"
}
@@ -140,23 +149,26 @@ general_output() {
REGION_FROM_CHECK=$REGION
fi
# Iterating over input modes
for MODE in "${MODES[@]}";
OIFS="${IFS}"
IFS=','
for MODE_TYPE in ${MODE}
do
if [ "${MODE}" == 'html' ]
IFS="${OIFS}"
if [ "${MODE_TYPE}" == 'html' ]
then
generateHtmlOutput "${CHECK_RESULT_EXTENDED}" "${CHECK_RESULT}"
elif [ "${MODE}" == 'csv' ]
elif [ "${MODE_TYPE}" == 'csv' ]
then
echo "${CSV_LINE}" >> "${OUTPUT_FILE_NAME}"."${EXTENSION_CSV}"
elif [ "${MODE}" == 'json' ]
elif [ "${MODE_TYPE}" == 'json' ]
then
generateJsonOutput "${CHECK_RESULT_EXTENDED}" "${CHECK_RESULT}" "$CHECK_RESOURCE_ID" >> "${OUTPUT_FILE_NAME}"."${EXTENSION_JSON}"
elif [ "${MODE}" == 'json-asff' ]
elif [ "${MODE_TYPE}" == 'json-asff' ]
then
JSON_ASFF_OUTPUT=$(generateJsonAsffOutput "${CHECK_RESULT_EXTENDED}" "PASSED" "$CHECK_RESOURCE_ID")
echo "${JSON_ASFF_OUTPUT}" >> "${OUTPUT_FILE_NAME}"."${EXTENSION_ASFF}"
if [[ "${SEND_TO_SECURITY_HUB}" -eq 1 ]]; then
sendToSecurityHub "${JSON_ASFF_OUTPUT}" "${REGION_FROM_CHECK}"
sendToSecurityHub "${JSON_ASFF_OUTPUT}" "${REGION_FROM_CHECK}"
fi
elif is_junit_output_enabled
then
@@ -164,7 +176,7 @@ general_output() {
then
output_junit_success "${CHECK_RESULT_EXTENDED}"
elif [ "${CHECK_RESULT}" == 'INFO' ]
then
then
output_junit_info "${CHECK_RESULT_EXTENDED}"
elif [ "${CHECK_RESULT}" == 'FAIL' ]
then
@@ -173,7 +185,7 @@ general_output() {
then
output_junit_skipped "${CHECK_RESULT_EXTENDED}"
fi
elif [ "${MODE}" == 'mono' ]
elif [ "${MODE_TYPE}" == 'mono' ]
then
echo " $COLOR_CODE ${CHECK_RESULT}! $NORMAL ${CHECK_RESULT_EXTENDED}">> "${OUTPUT_FILE_NAME}"."${EXTENSION_TEXT}"
fi
@@ -195,11 +207,13 @@ textPass(){
CHECK_RESOURCE_ID="${3}"
CHECK_REGION="${2}"
PASS_COUNTER=$((PASS_COUNTER+1))
if is_quiet "${QUIET}"
then
return
fi
general_output "${CHECK_RESULT}" "${CHECK_RESULT_EXTENDED}" "${CHECK_RESOURCE_ID}" "${CHECK_REGION}"
}
@@ -218,15 +232,17 @@ textInfo(){
}
textFail(){
CHECK_RESULT='FAIL'
CHECK_RESULT_EXTENDED="${1}"
CHECK_RESOURCE_ID="${3}"
CHECK_REGION="${2}"
FAIL_COUNTER=$((FAIL_COUNTER+1))
# Check if resources are whitelisted
CHECK_RESULT=$(allowlist_check "${CHECK_RESULT_EXTENDED}")
# only set non-0 exit code on FAIL mode, WARN is ok
if [[ "${CHECK_RESULT}" == "FAIL" ]]
then
@@ -237,11 +253,13 @@ textFail(){
}
textTitle(){
TITLE_ID="${1}"
TITLE_TEXT="${2}"
CHECK_ASFF_COMP_TYPE="${6}"
CHECK_ASFF_COMP_TYPE="${6}"
CHECKS_COUNTER=$((CHECKS_COUNTER+1))
if [[ $NUMERAL ]]; then
# Left-pad the check ID with zeros to simplify sorting, e.g. 1.1 -> 1.01
TITLE_ID=$(awk -F'.' '{ printf "%d.%02d", $1, $2 }' <<< "$TITLE_ID")
@@ -257,7 +275,7 @@ textTitle(){
SUPPORT) ITEM_CIS_LEVEL="Support";;
*) ITEM_CIS_LEVEL="Unspecified or Invalid";;
esac
echo -e "$TITLE_ID $TITLE_TEXT - $CHECK_SERVICENAME $CHECK_SEVERITY"
}
@@ -321,14 +339,14 @@ generateJsonAsffOutput(){
# Replace any successive non-conforming characters with a single underscore
local message=$1
local status=$2
#Checks to determine if the rule passes in a resource name that prowler uses to track the AWS Resource for allowlisting purposes
if [[ -z $3 ]]; then
local resource_id="NONE_PROVIDED"
else
local resource_id=$3
fi
if [[ "$status" == "FAIL" ]]; then
status="FAILED"
fi
@@ -384,7 +402,7 @@ generateJsonAsffOutput(){
"Status": $STATUS,
"RelatedRequirements": [ $COMPLIANCE_RELATED_REQUIREMENTS ]
}
}'
}
@@ -404,7 +422,7 @@ generateHtmlOutput(){
if [[ $status == "WARN" ]];then
local ROW_CLASS='table-warning'
fi
local CHECK_SEVERITY="$(echo $CHECK_SEVERITY | sed 's/[][]//g')"
echo '<tr class="'$ROW_CLASS'">' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
@@ -425,4 +443,4 @@ generateHtmlOutput(){
echo ' <td>'$CHECK_RESOURCE_ID'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
echo '</tr>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
echo '' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
}
}
+6 -16
View File
@@ -11,21 +11,8 @@
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
if [[ $OUTPUT_BUCKET ]]; then
# output mode has to be set to other than text
if [[ "${MODES[*]}" =~ "text" ]]; then
echo "$OPTRED ERROR!$OPTNORMAL - Mode (-M) can't be text when using custom output bucket. Use -h for help."
exit 1
else
# need to make sure last / is not set to avoid // in S3
if [[ $OUTPUT_BUCKET == *"/" ]]; then
OUTPUT_BUCKET=${OUTPUT_BUCKET::-1}
fi
fi
fi
copyToS3() {
# Prowler will copy each format to its own folder in S3, that is for better handling
# Prowler will copy each format to its own folder in S3, that is for better handling
# and processing by Quicksight or others.
# Also, check if -F was introduced
if [ -n "${OUTPUT_FILE_NAME+x}" ]; then
@@ -34,9 +21,12 @@ copyToS3() {
OUTPUT_PATH="$OUTPUT_DIR/prowler-output-${ACCOUNT_NUM}-${OUTPUT_DATE}"
fi
for output_format in "${MODES[@]}";
OIFS="${IFS}"
IFS=','
for MODE_TYPE in ${MODE}
do
case ${output_format} in
IFS="${OIFS}"
case ${MODE_TYPE} in
csv)
s3cp "${OUTPUT_PATH}" "${EXTENSION_CSV}"
;;
-32
View File
@@ -1,32 +0,0 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
# detector of python and boto3
pythonDetector(){
PYTHON_BIN=$(which python)
PYTHON_PIP_BOTO3=$(pip list|grep boto3)
if [ -z "${PYTHON_BIN}" ]; then
echo -e "\n$RED ERROR!$NORMAL python not found. Make sure it is installed correctly and in your \$PATH\n"
EXITCODE=1
exit $EXITCODE
else
PYTHON_INSTALLED=1
if [ -z "${PYTHON_PIP_BOTO3}" ]; then
echo -e "\n$RED ERROR!$NORMAL python library boto3 not found. Make sure it is installed correctly\n"
EXITCODE=1
exit $EXITCODE
else
PYTHON_PIP_BOTO3_INSTALLED=1
fi
fi
}
+2 -2
View File
@@ -54,8 +54,8 @@ scoring(){
echo -e "$BLUE------------------------------------------------------------------ $NORMAL"
echo -e " * the higher the better (0 to 100)$NORMAL"
echo -e " Prowler scoring uses any check, including CIS not scored checks$NORMAL"
fi
if [[ "${MODES[@]}" =~ "html" ]]; then
fi
if [[ "${MODE}" =~ "html" ]]; then
replace_sed 's/PROWLER_SCORE/'$PROWLER_SCORE'/g' ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
replace_sed 's/PASS_COUNTER/'$PASS_COUNTER'/g' ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
replace_sed 's/TOTAL_RESOURCES/'$TOTAL_RESOURCES'/g' ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
+15 -16
View File
@@ -13,41 +13,40 @@
# detector and configuration of detect-secrets
secretsDetector(){
PYTHON_PIP_DETECTSECRETS=$(which detect-secrets)
if [ -z "${PYTHON_PIP_DETECTSECRETS}" ]; then
if ! command -v "detect-secrets" &>/dev/null
then
echo -e "\n$RED ERROR!$NORMAL python library detect-secrets not found. Make sure it is installed correctly and in your \$PATH\n"
EXITCODE=241
exit $EXITCODE
else
SECRETS_TEMP_FOLDER="$PROWLER_DIR/secrets-$ACCOUNT_NUM"
if [[ ! -d $SECRETS_TEMP_FOLDER ]]; then
mkdir $SECRETS_TEMP_FOLDER
if [[ ! -d "${SECRETS_TEMP_FOLDER}" ]]; then
mkdir "${SECRETS_TEMP_FOLDER}"
fi
PYTHON_PIP_DETECTSECRETS_INSTALLED=1
# Sets the entropy limit for high entropy base64 strings from
# environment variable BASE64_LIMIT.
# Sets the entropy limit for high entropy base64 strings from environment variable BASE64_LIMIT.
# Value must be between 0.0 and 8.0, defaults is 4.5.
# Sets the entropy limit for high entropy hex strings from
# environment variable HEX_LIMIT.
# Sets the entropy limit for high entropy hex strings from environment variable HEX_LIMIT.
# Value must be between 0.0 and 8.0, defaults is 3.0.
case $1 in
file )
# this is to scan a file
detect-secrets scan --hex-limit ${HEX_LIMIT:-3.0} --base64-limit ${BASE64_LIMIT:-4.5} $2 | \
jq -r '.results[]|.[] | [.line_number, .type]|@csv' | wc -l
#jq -r '.results[] | .[] | "\(.line_number)\t\(.type)"'
detect-secrets scan --hex-limit "${HEX_LIMIT:-3.0}" --base64-limit "${BASE64_LIMIT:-4.5}" "${2}" \
| jq -r '.results[]|.[] | [.line_number, .type]|@csv' \
| wc -l
# jq -r '.results[] | .[] | "\(.line_number)\t\(.type)"'
# this command must return values in two colums:
# line in file and type of secrets found
;;
string )
# this is to scan a given string
detect-secrets scan --hex-limit ${HEX_LIMIT:-3.0} --base64-limit ${BASE64_LIMIT:-4.5} --string $2 | \
grep True| wc -l
detect-secrets scan --hex-limit "${HEX_LIMIT:-3.0}" --base64-limit "${BASE64_LIMIT:-4.5}" --string "${2}" \
| grep True | wc -l
;;
folder )
# this is to scan a given folder with all lambda files
detect-secrets scan --hex-limit ${HEX_LIMIT:-3.0} --base64-limit ${BASE64_LIMIT:-4.5} --all-files $2 | \
jq -r '.results[]|.[] | [.line_number, .type]|@csv' | wc -l
detect-secrets scan --hex-limit "${HEX_LIMIT:-3.0}" --base64-limit "${BASE64_LIMIT:-4.5}" --all-files "${2}" \
| jq -r '.results[]|.[] | [.line_number, .type]|@csv' | wc -l
;;
esac
fi
+2 -3
View File
@@ -16,7 +16,7 @@
checkSecurityHubCompatibility(){
local regx
if [[ "${MODE}" != "json-asff" ]]; then
if [[ ! "${MODE}" =~ "json-asff" ]]; then
echo -e "\n$RED ERROR!$NORMAL Output can only be sent to Security Hub when the output mode is json-asff, i.e. -M json-asff -S\n"
EXITCODE=1
exit $EXITCODE
@@ -71,7 +71,7 @@ resolveSecurityHubPreviousFails(){
if [[ $SECURITY_HUB_PREVIOUS_FINDINGS != "[]" ]]; then
FINDINGS_COUNT=$(echo $SECURITY_HUB_PREVIOUS_FINDINGS | jq '. | length')
for i in $(seq 0 50 $FINDINGS_COUNT);
do
do
BATCH_FINDINGS=$(echo $SECURITY_HUB_PREVIOUS_FINDINGS | jq -c '.['"$i:$i+50"']')
BATCH_FINDINGS_COUNT=$(echo $BATCH_FINDINGS | jq '. | length')
if [ "$BATCH_FINDINGS_COUNT" -gt 0 ]; then
@@ -94,7 +94,6 @@ sendToSecurityHub(){
local finding_id=$(echo ${findings} | jq -r .Id )
SECURITYHUB_NEW_FINDINGS_IDS+=( "$finding_id" )
BATCH_IMPORT_RESULT=$($AWSCLI securityhub --region "$region" $PROFILE_OPT batch-import-findings --findings "${findings}" 2>&1)
# Check for success if imported
if [[ -z "${BATCH_IMPORT_RESULT}" ]] || ! jq -e '.SuccessCount == 1' <<< "${BATCH_IMPORT_RESULT}" > /dev/null 2>&1; then
echo "${BAD} ERROR! Failed to send check output to AWS Security Hub.${NORMAL}"
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env bash
# Copyright 2018 Toni de la Fuente
# Prowler is a tool that provides automate auditing and hardening guidance of an
# AWS account. It is based on AWS-CLI commands. It follows some guidelines
# present in the CIS Amazon Web Services Foundations Benchmark at:
# https://d0.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
# Contact the author at https://blyx.com/contact
# and open issues or ask questions at https://github.com/prowler-cloud/prowler
# Code is licensed as Apache License 2.0 as specified in
# each file. You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Show the title of a group, by their numerical id
show_group_title() {
textTitle "${GROUP_NUMBER[$1]}" "${GROUP_TITLE[$1]}"
}
# Show al group titles
show_all_group_titles() {
local group_index
for group_index in "${!GROUP_TITLE[@]}"; do
show_group_title "$group_index"
done
exit 0
}
# Show the titles of either all checks or only those in the specified group
show_all_check_titles() {
for CHECK_ID in ${TOTAL_CHECKS[*]}
do
show_check_title "${CHECK_ID}"
done
exit 0
}
# Function to show the title of the check, and optionally which group(s) it belongs to
# using this way instead of arrays to keep bash3 (osx) and bash4(linux) compatibility
show_check_title() {
local check_id=CHECK_ID_$1
local check_title=CHECK_TITLE_$1
local check_scored=CHECK_SCORED_$1
local check_cis_level=CHECK_CIS_LEVEL_$1
local check_asff_compliance_type=CHECK_ASFF_COMPLIANCE_TYPE_$1
local check_severity=CHECK_SEVERITY_$1
local check_servicename=CHECK_SERVICENAME_$1
local group_ids
local group_index
# This shows ASFF_COMPLIANCE_TYPE if group used is ens, this si used to show ENS compliance ID control, can be used for other compliance groups as well.
if [[ ${GROUP_ID_READ} == "ens" ]];then
textTitle "${!check_id}" "${!check_title}" "${!check_scored}" "${!check_cis_level}" "$group_ids" "(${!check_asff_compliance_type})"
else
textTitle "${!check_id}" "${!check_title}" "${!check_servicename}" "${!check_severity}" "$group_ids" "${!check_cis_level}"
fi
}
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
jq_detector() {
if ! command -v jq &>/dev/null
then
echo -e "\n$RED ERROR!$NORMAL jq not found. Make sure it is installed correctly and in your \$PATH\n"
EXITCODE=1
exit $EXITCODE
fi
}
curl_detector() {
if ! command -v curl &>/dev/null
then
echo -e "\n$RED ERROR!$NORMAL curl not found. Make sure it is installed correctly and in your \$PATH\n"
EXITCODE=1
exit $EXITCODE
fi
}
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env bash
# Copyright 2018 Toni de la Fuente
# Prowler is a tool that provides automate auditing and hardening guidance of an
# AWS account. It is based on AWS-CLI commands. It follows some guidelines
# present in the CIS Amazon Web Services Foundations Benchmark at:
# https://d0.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
# Contact the author at https://blyx.com/contact
# and open issues or ask questions at https://github.com/prowler-cloud/prowler
# Code is licensed as Apache License 2.0 as specified in
# each file. You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
clean_up() {
rm -f /tmp/prowler*.policy.*
# puts the AWS_DEFAULT_OUTPUT back to what it was at the start
if [ -z "$ORIGINAL_OUTPUT" ]; then
export AWS_DEFAULT_OUTPUT="$ORIGINAL_OUTPUT"
else
unset AWS_DEFAULT_OUTPUT
fi
# Delete temporary report file
if [[ $KEEPCREDREPORT -ne 1 ]]; then
rm -fr $TEMP_REPORT_FILE
fi
cleanSTSAssumeFile
}
handle_ctrl_c() {
clean_up
scoring
exit $EXITCODE
}
+233
View File
@@ -0,0 +1,233 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
# Copy to S3 without assuming role with -D
validate_copy_to_s3_no_assume() {
if [[ $OUTPUT_BUCKET_NOASSUME ]]; then
restoreInitialAWSCredentials
fi
}
# Copy to S3 assuming role with -B
copy_to_s3_assume() {
if [[ $OUTPUT_BUCKET ]]; then
copyToS3
fi
}
# Validate custom checks with -x
validate_custom_checks() {
if [[ $EXTERNAL_CHECKS_PATH ]]
then
custom_checks
fi
}
# AllowList option input with -w
validate_allowlist() {
# Pre-process allowlist file if supplied
if [[ -n "$ALLOWLIST_FILE" ]]
then
allowlist
fi
}
# List checks input with -l
validate_list_checks() {
if [[ ${LIST_CHECKS} -eq 1 ]]
then
show_all_check_titles
fi
}
# List groups input with -g
validate_list_groups() {
if [[ ${LIST_GROUPS} -eq 1 ]]
then
show_all_group_titles
fi
}
# Extract organizations information with -O
validate_organizations() {
# First, check AWS Organizations Metadata
if [[ -n "${MANAGEMENT_ACCOUNT_ID}" && -n "${ROLE_TO_ASSUME}" ]]
then
# Backing up initial credentials
backupInitialAWSCredentials
# Backing up initial account to assume
INITIAL_ACCOUNT_TO_ASSUME="${ACCOUNT_TO_ASSUME}"
# Set the new account to assume to recover AWS Organizations Metadata
ACCOUNT_TO_ASSUME="${MANAGEMENT_ACCOUNT_ID}"
# Recover AWS Organizations Metadata
get_orgs_account_details
# Restoring account to assume to -A field after getting account metadata
ACCOUNT_TO_ASSUME="${INITIAL_ACCOUNT_TO_ASSUME}"
# Restoring initial credentials
restoreInitialAWSCredentials
fi
if [[ -n "${ACCOUNT_TO_ASSUME}" || -n "${ROLE_TO_ASSUME}" ]]
then
backupInitialAWSCredentials
assume_role
fi
}
# JUnit output if -M junit-xml
validate_junit_output() {
if is_junit_output_enabled
then
prepare_junit_output
fi
}
# Validate Security Hub with -S
validate_security_hub() {
if [[ "$SEND_TO_SECURITY_HUB" -eq 1 ]]
then
checkSecurityHubCompatibility
fi
}
# Validate output bucket with -B
validate_output_bucket() {
# Output bucket parsing input checking
if [[ $OUTPUT_BUCKET ]]
then
# output mode has to be set to other than text
if [[ "${MODE}" =~ "text" ]]
then
echo "$OPTRED ERROR!$OPTNORMAL - Mode (-M) can't be text when using custom output bucket. Use -h for help."
exit 1
else
# need to make sure last / is not set to avoid // in S3
if [[ $OUTPUT_BUCKET == *"/" ]]
then
OUTPUT_BUCKET=${OUTPUT_BUCKET::-1}
fi
fi
fi
}
# Validate custom output filename with -F
validate_custom_output_filename() {
# Create output file name
if [ -n "${OUTPUT_FILE_NAME}" ]
then
OUTPUT_FILE_NAME="${OUTPUT_DIR}/$OUTPUT_FILE_NAME"
else
OUTPUT_FILE_NAME="${OUTPUT_DIR}/prowler-output-${ACCOUNT_NUM}-${OUTPUT_DATE}"
fi
}
# Validate custom output directory with -o
validate_custom_output_directory() {
# Check output dir custom
if [ -n "${OUTPUT_DIR_CUSTOM}" ]
then
# If dir custom, mode has to be enabled (if you provide a custom directory it's because you want to store something)
if [[ ! "${MODE}" =~ 'text' ]]
then
# Check if custom folder exists
if [ ! -d "${OUTPUT_DIR_CUSTOM}" ]
then
echo "$OPTRED ERROR!$OPTNORMAL directory \"$OUTPUT_DIR_CUSTOM\" does not exist."
exit 1
else
OUTPUT_DIR=$OUTPUT_DIR_CUSTOM
fi
else
echo "$OPTRED ERROR!$OPTNORMAL - When using custom directory output Mode (-M) has to be set as well. Use -h for help."
exit 1
fi
# If custom output dir is not provided output dir -> default
else
OUTPUT_DIR="${PROWLER_DIR}/output"
if [ ! -d "${OUTPUT_DIR}" ]
then
mkdir -p "${OUTPUT_DIR}"
fi
fi
}
# Validate output modes with -M
validate_modes() {
if [ -n "${MODE}" ]
then
AVAILABLE_OUTPUT_MODES="mono|text|csv|json|json-asff|junit-xml|html"
OIFS="${IFS}"
IFS=','
for MODE_TYPE in ${MODE}
do
if ! grep -w -q -E "${AVAILABLE_OUTPUT_MODES}" <<< "${MODE_TYPE}"
then
echo -e "${OPTRED}ERROR!$OPTNORMAL Invalid output mode. Choose text, mono, csv, json, json-asff, junit-xml or html. ./prowler -h for help"
EXITCODE=1
exit $EXITCODE
fi
done
IFS="${OIFS}"
fi
}
# Validate database with -d
validate_database() {
# Check database providers
if [[ ${DATABASE_PROVIDER} ]]
then
# Check if input provider is supported
if ! grep -w -q -E "${DATABASE_PROVIDER}" <<< "${SUPPORTED_DB_PROVIDERS}"
then
db_exit_abnormally "${DATABASE_PROVIDER}" "DB provider not supported, providers supported: ${SUPPORTED_DB_PROVIDERS} - EXITING!"
elif ! command -v "psql" > /dev/null 2>&1
then
db_exit_abnormally "postgresql" "psql tool not installed or not found- EXITING!"
elif [[ $(find "${HOME}/.pgpass" -perm 600 2>/dev/null) != "$HOME/.pgpass" ]]
then
db_exit_abnormally "postgresql" ".pgpass file not found at $HOME/.pgpass or .pgpass file permissions not matching 600 - EXITING!"
else
IFS=':' read -r PSQL_HOSTNAME PSQL_PORT PSQL_DATABASE PSQL_USER PSQL_PASSWORD PSQL_TABLE < "$HOME/.pgpass"
if [[ ! ${PSQL_HOSTNAME} ]] || [[ ! ${PSQL_PORT} ]] || [[ ! ${PSQL_DATABASE} ]] || [[ ! ${PSQL_USER} ]] || [[ ! ${PSQL_PASSWORD} ]] || [[ ! ${PSQL_TABLE} ]]
then
db_exit_abnormally "postgresql" "Empty field into ${HOME}/.pgpass file, all fields must be filled. Please check Prowler README about .pgpass file format - EXITING!"
fi
export PSQL_USER
export PSQL_HOSTNAME
export PSQL_TABLE
# Once all the variables are defined and exported test if database instance is reachable
if ! pg_isready -q -U "${PSQL_USER}" -h "${PSQL_HOSTNAME}" -p "${PSQL_PORT}"
then
db_exit_abnormally "postgresql" "Database listening on host ${PSQL_HOSTNAME} on port ${PSQL_PORT} connected as user ${PSQL_USER} is unreachable - EXITING!"
# If database instance is ready time to check credentials
elif ! PGPASSWORD="${PSQL_PASSWORD}" psql -U "${PSQL_USER}" -h "${PSQL_HOSTNAME}" -c "\q" > /dev/null 2>&1
then
db_exit_abnormally "postgresql" "User ${PSQL_USER} invalid or invalid credentials, please check ${HOME}/.pgpass file - EXITING!"
# If credentials are ok -> database exists?
elif ! PGPASSWORD="${PSQL_PASSWORD}" psql -U "${PSQL_USER}" -h "${PSQL_HOSTNAME}" "${PSQL_DATABASE}" -c "\q" > /dev/null 2>&1
then
db_exit_abnormally "postgresql" "Database not exists, please check ${HOME}/.pgpass file - EXITING!"
# and finally, if database exists -> table exists ?
elif ! PGPASSWORD="${PSQL_PASSWORD}" psql -U "${PSQL_USER}" -h "${PSQL_HOSTNAME}" "${PSQL_DATABASE}" -c "SELECT * FROM ${PSQL_TABLE};" > /dev/null 2>&1
then
db_exit_abnormally "postgresql" "Table ${PSQL_TABLE} not exists, please check ${HOME}/.pgpass file - EXITING!"
fi
fi
fi
}
+29 -65
View File
@@ -19,77 +19,41 @@
# when invoking regions with -r, those regions with STS disabled make GETCALLER fail then
# this if will filter them out (Africa (Cape Town), Asia Pacific (Hong Kong), Europe (Milan) and Middle East (Bahrain) ):
print_whoami(){
echo -e "\n This report is being generated using credentials below:\n"
echo -e " AWS-CLI Profile: ${NOTICE}[${PROFILE}]${NORMAL} AWS API Region: ${NOTICE}[${REGION}]${NORMAL} AWS Filter Region: ${NOTICE}[${FILTERREGION:-all}]${NORMAL}"
echo -e " AWS Account: ${NOTICE}[${ACCOUNT_NUM}]${NORMAL} UserId: ${NOTICE}[${USER_ID}]${NORMAL}"
echo -e " Caller Identity ARN: ${NOTICE}[${CALLER_ARN}]${NORMAL}\n"
}
case "$REGION" in
me-south-1|eu-south-1|ap-east-1|af-south-1)
REGION_FOR_STS="us-east-1"
;;
*)
REGION_FOR_STS=$REGION
;;
esac
get_aws_credentials() {
case "$REGION" in
me-south-1|eu-south-1|ap-east-1|af-south-1)
REGION_FOR_STS="us-east-1"
;;
*)
REGION_FOR_STS=$REGION
;;
esac
GETCALLER=$($AWSCLI sts get-caller-identity $PROFILE_OPT --output json --region $REGION_FOR_STS 2>&1)
ret=$?
if [[ $ret -ne 0 ]]; then
if [[ $PRINTCHECKSONLY || $PRINTGROUPSONLY ]]; then
echo Listing...
else
# Failed to get own identity ... exit
if ! GETCALLER=$("${AWSCLI}" sts get-caller-identity \
${PROFILE_OPT} \
--output text \
--region "${REGION_FOR_STS}" \
--query '[Arn,UserId,Account]' 2>&1)
then
echo -e "$RED ERROR Getting credentials to run Prowler - EXITING! $NORMAL"
EXITCODE=2
exit $EXITCODE
fi
fi
read -r CALLER_ARN USER_ID ACCOUNT_NUM <<< "${GETCALLER}"
if [[ $ACCOUNT_TO_ASSUME ]]; then
ACCOUNT_NUM=$ACCOUNT_TO_ASSUME
else
ACCOUNT_NUM=$(echo $GETCALLER | jq -r '.Account' 2>&1)
fi
CALLER_ARN=$(echo $GETCALLER | jq -r '.Arn' 2>&1)
USER_ID=$(echo $GETCALLER | jq -r '.UserId' 2>&1)
AWS_PARTITION=$(echo $CALLER_ARN| cut -d: -f2)
getWhoami(){
if [[ "$MODE" == "csv" ]]; then
if [[ 255 -eq $? ]]; then
# Failed to get own identity ... exit
echo "ERROR WITH $PROFILE CREDENTIALS - EXITING!"
>&2 echo "ERROR WITH $PROFILE CREDENTIALS - EXITING!"
EXITCODE=2
exit $EXITCODE
fi
# textTitle "0.0" "Show report generation info" "NOT_SCORED" "SUPPORT"
# textInfo "ARN: $CALLER_ARN TIMESTAMP: $SCRIPT_START_TIME"
elif [[ "$MODE" == "json" || "$MODE" == "json-asff" ]]; then
:
else
echo ""
echo -e " This report is being generated using credentials below:\n"
echo -e " AWS-CLI Profile: $NOTICE[$PROFILE]$NORMAL AWS API Region: $NOTICE[$REGION]$NORMAL AWS Filter Region: $NOTICE[${FILTERREGION:-all}]$NORMAL"
if [[ $MONOCHROME -eq 1 ]]; then
echo -e " AWS Account: $NOTICE[$ACCOUNT_NUM]$NORMAL UserId: $NOTICE[$USER_ID]$NORMAL"
echo -e " Caller Identity ARN: $NOTICE[$CALLER_ARN]$NORMAL"
if [[ 255 -eq $? ]]; then
# Failed to get own identity ... exit
echo "ERROR WITH $PROFILE CREDENTIALS - EXITING!"
>&2 echo "ERROR WITH $PROFILE CREDENTIALS - EXITING!"
exit 2
fi
else
echo -e " AWS Account: $NOTICE[$ACCOUNT_NUM]$NORMAL UserId: $NOTICE[$USER_ID]$NORMAL"
echo -e " Caller Identity ARN: $NOTICE[$CALLER_ARN]$NORMAL"
if [[ 255 -eq $? ]]; then
# Failed to get own identity ... exit
echo variable $PROFILE_OPT
echo "ERROR WITH $PROFILE CREDENTIALS - EXITING!"
>&2 echo "ERROR WITH $PROFILE CREDENTIALS - EXITING!"
EXITCODE=2
exit $EXITCODE
fi
echo ""
fi
if [[ $ACCOUNT_TO_ASSUME ]]
then
ACCOUNT_NUM=$ACCOUNT_TO_ASSUME
fi
# Set AWS partition
AWS_PARTITION=$(cut -d: -f2 <<< "${CALLER_ARN}")
export AWS_PARTITION
}
+108 -629
View File
@@ -22,62 +22,70 @@
# All there legs and lashes
# I've just got to find my way...
OPTRED=""
OPTNORMAL=""
# Set the defaults variables
PROWLER_VERSION=2.10.0-25May2022
PROWLER_DIR=$(dirname "$0")
REGION=""
FILTERREGION=""
MAXITEMS=100
MONOCHROME=0
MODE="text"
QUIET=0
SEP=','
KEEPCREDREPORT=0
EXITCODE=0
SEND_TO_SECURITY_HUB=0
FAILED_CHECK_FAILED_SCAN=1
PROWLER_START_TIME=$( date -u +"%Y-%m-%dT%H:%M:%S%z" )
OUTPUT_DATE=$(date -u +"%Y%m%d%H%M%S")
TITLE_ID=""
TITLE_TEXT="CALLER ERROR - UNSET TITLE"
ALLOWLIST_FILE=""
TOTAL_CHECKS=()
############################################################
# DEPENDENCIES
. $PROWLER_DIR/include/default_variables
. $PROWLER_DIR/include/colors
. $PROWLER_DIR/include/os_detector
. $PROWLER_DIR/include/aws_profile_loader
. $PROWLER_DIR/include/awscli_detector
. $PROWLER_DIR/include/whoami
. $PROWLER_DIR/include/assume_role
. $PROWLER_DIR/include/csv_header
. $PROWLER_DIR/include/banner
. $PROWLER_DIR/include/html_report
. $PROWLER_DIR/include/tools_detector
. $PROWLER_DIR/include/outputs_bucket
. $PROWLER_DIR/include/outputs
. $PROWLER_DIR/include/credentials_report
. $PROWLER_DIR/include/scoring
. $PROWLER_DIR/include/secrets_detector
. $PROWLER_DIR/include/check_creds_last_used
. $PROWLER_DIR/include/check3x
. $PROWLER_DIR/include/connection_tests
. $PROWLER_DIR/include/securityhub_integration
. $PROWLER_DIR/include/junit_integration
. $PROWLER_DIR/include/organizations_metadata
. $PROWLER_DIR/include/custom_checks
. $PROWLER_DIR/include/allowlist
. $PROWLER_DIR/include/db_connector
. $PROWLER_DIR/include/show_titles
. $PROWLER_DIR/include/loader
. $PROWLER_DIR/include/execute_check
. $PROWLER_DIR/include/validate_options
. $PROWLER_DIR/include/traps
############################################################
# Check external tools
jq_detector
curl_detector
# Ensures command output will always be set to JSON.
# If the default value is already set, ORIGINAL_OUTPUT will be used to store it and reset it at cleanup
if [[ -z "${AWS_DEFAULT_OUTPUT}" ]]; then
ORIGINAL_OUTPUT=$AWS_DEFAULT_OUTPUT
export AWS_DEFAULT_OUTPUT="json"
else
export AWS_DEFAULT_OUTPUT="json"
fi
# Command usage menu
############################################################
# USAGE
usage(){
echo "
USAGE:
`basename $0` [ -p <profile> -r <region> -h ]
$(basename "${0}") [ -p <profile> -r <region> -h ]
Options:
-p <profile> Specify your AWS profile to use.
(i.e.: default)
-r <region> Specify an AWS region to direct API requests to.
(i.e.: us-east-1), all regions are checked anyway if the check requires it.
-c <check_id> Specify one or multiple check ids separated by commas, to see all available checks use "-l" option.
(i.e.: "check11" for check 1.1 or "extra71,extra72" for extra check 71 and extra check 72)
-c <check_id> Specify one or multiple check ids separated by commas, to see all available checks use \"-l\" option.
(i.e.: \"check11\" for check 1.1 or \"extra71,extra72\" for extra check 71 and extra check 72)
-C Checklist file. See checklist.txt for reference and format.
(i.e.: checklist.txt)
-g <group_id> Specify a group of checks by id, to see all available group of checks use "-L".
(i.e.: "group3" for entire section 3, "cislevel1" for CIS Level 1 Profile Definitions or "forensics-ready")
-g <group_id> Specify a group of checks by id, to see all available group of checks use \"-L\".
(i.e.: \"group3\" for entire section 3, \"cislevel1\" for CIS Level 1 Profile Definitions or \"forensics-ready\")
-f <filterregion> Specify an AWS region to run checks against.
(i.e.: us-west-1 or for multiple regions use single quote like 'us-west-1 us-west-2')
-m <maxitems> Specify the maximum number of items to return for long-running requests (default: 100).
-M <mode> Output or report mode: text (default), mono, html, json, json-asff, junit-xml, csv. They can be used combined comma separated.
(i.e.: "html,json"; files created in background; progress on stdout)
(i.e.: \"html,json\"; files created in background; progress on stdout)
-k Keep the credential report for debugging.
-n Show check numbers to sort easier.
(i.e.: 1.01 instead of 1.1)
@@ -89,9 +97,9 @@ USAGE:
-b Do not print Prowler banner.
-s Show scoring report (it is included by default in the html report).
-S Send check output to AWS Security Hub. Only valid when the output mode is json-asff
(i.e. "-M json-asff -S").
(i.e. \"-M json-asff -S\").
-x Specify external directory with custom checks. S3 URI is supported.
(i.e. /my/own/checks or s3://bucket/prefix/checks, files must start by "check").
(i.e. /my/own/checks or s3://bucket/prefix/checks, files must start by \"check\").
-q Get only FAIL findings, will show WARNINGS when a resource is excluded.
-A Account id for the account where to assume a role, requires -R.
(i.e.: 123456789012)
@@ -100,8 +108,8 @@ USAGE:
-T Session duration given to that role credentials in seconds, default 1h (3600) recommended 12h, optional with -R and -A.
(i.e.: 43200)
-I External ID to be used when assuming roles (not mandatory), requires -A and -R.
-w Allowlist file. See allowlist_sample.txt for reference and format. S3 URI is supported.
(i.e.: allowlist_sample.txt or s3://bucket/prefix/allowlist_sample.txt)
-w Allowlist file. See allowlist_sample.txt for reference and format. S3 URI and DynamoDB table ARNs are also supported as allowlist file.
(i.e.: allowlist_sample.txt or s3://bucket/prefix/allowlist_sample.txt or arn:aws:dynamodb:us-east-1:111111222222:table/allowlist)
-N <shodan_api_key> Shodan API key used by check extra7102.
-o Custom output directory, if not specified will use default prowler/output, requires -M <mode>.
(i.e.: -M csv -o /tmp/reports/)
@@ -110,11 +118,11 @@ USAGE:
-D Same as -B but do not use the assumed role credentials to put objects to the bucket, instead uses the initial credentials.
-F Custom output report name, if not specified will use default output/prowler-output-ACCOUNT_NUM-OUTPUT_DATE.format.
-z Failed checks do not trigger exit code 3.
-Z Specify one or multiple check ids separated by commas that will trigger exit code 3 if they fail. Unspecified checks will not trigger exit code 3. This will override "-z".
(i.e.: "-Z check11,check12" will cause check11 and/or check12 to trigger exit code 3)
-Z Specify one or multiple check ids separated by commas that will trigger exit code 3 if they fail. Unspecified checks will not trigger exit code 3. This will override \"-z\".
(i.e.: \"-Z check11,check12\" will cause check11 and/or check12 to trigger exit code 3)
-O <mgmnt acct ID> Specify AWS Organizations management account ID. Used to get account details, requires -R.
(requires organizations:ListAccounts* and organizations:ListTagsForResource)
-a <aws_cli_cmd> Build your own on-the-fly custom check by specifying the AWS CLI command to execute. Requires "-c extra9999". Omit the "aws" command and only use its parameters within quotes.
-a <aws_cli_cmd> Build your own on-the-fly custom check by specifying the AWS CLI command to execute. Requires \"-c extra9999\". Omit the \"aws\" command and only use its parameters within quotes.
Do not nest quotes in the aws parameter. Note that --output text is already included in the check.
i,e. -a 'ec2 describe-security-groups --filters Name=ip-permission.to-port,Values=80 --query SecurityGroups[*].GroupId[]]'
-V Show version number & exit.
@@ -124,6 +132,11 @@ USAGE:
exit
}
############################################################
# This function comes from include/awscli_detector
set_aws_default_output
# Parse Prowler command line options
while getopts ":hlLkqp:r:c:C:g:f:m:M:E:x:enbVsSI:A:R:T:w:N:o:B:D:F:zZ:O:a:d:" OPTION; do
case $OPTION in
h )
@@ -132,10 +145,10 @@ while getopts ":hlLkqp:r:c:C:g:f:m:M:E:x:enbVsSI:A:R:T:w:N:o:B:D:F:zZ:O:a:d:" OP
exit $EXITCODE
;;
l )
PRINTCHECKSONLY=1
LIST_CHECKS=1
;;
L )
PRINTGROUPSONLY=1
LIST_GROUPS=1
;;
k )
KEEPCREDREPORT=1
@@ -160,7 +173,7 @@ while getopts ":hlLkqp:r:c:C:g:f:m:M:E:x:enbVsSI:A:R:T:w:N:o:B:D:F:zZ:O:a:d:" OP
FILTERREGION=$OPTARG
;;
m )
MAXITEMS=$OPTARG
export MAXITEMS=$OPTARG
;;
M )
MODE=$OPTARG
@@ -210,7 +223,7 @@ while getopts ":hlLkqp:r:c:C:g:f:m:M:E:x:enbVsSI:A:R:T:w:N:o:B:D:F:zZ:O:a:d:" OP
ALLOWLIST_FILE=$OPTARG
;;
N )
SHODAN_API_KEY=$OPTARG
export SHODAN_API_KEY=$OPTARG
;;
o )
OUTPUT_DIR_CUSTOM=$OPTARG
@@ -235,7 +248,7 @@ while getopts ":hlLkqp:r:c:C:g:f:m:M:E:x:enbVsSI:A:R:T:w:N:o:B:D:F:zZ:O:a:d:" OP
MANAGEMENT_ACCOUNT_ID=$OPTARG
;;
a )
CUSTOM_CMD=$OPTARG
export CUSTOM_CMD=$OPTARG
;;
d )
DATABASE_PROVIDER=$OPTARG
@@ -256,25 +269,19 @@ while getopts ":hlLkqp:r:c:C:g:f:m:M:E:x:enbVsSI:A:R:T:w:N:o:B:D:F:zZ:O:a:d:" OP
;;
esac
done
############################################################
# PROWLER MAIN
clean_up() {
rm -f /tmp/prowler*.policy.*
# in case html output is used, make sure it closes html file properly
if [[ "${MODES[@]}" =~ "html" ]]; then
addHtmlFooter >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
fi
# puts the AWS_DEFAULT_OUTPUT back to what it was at the start
if [ -z "$ORIGINAL_OUTPUT" ]; then
export AWS_DEFAULT_OUTPUT="$ORIGINAL_OUTPUT"
else
unset AWS_DEFAULT_OUTPUT
fi
}
# Set Prowler Colors
set_colors
handle_ctrl_c() {
clean_up
exit $EXITCODE
}
# Show Prowler Banner
prowlerBanner
# Load Prowler Groups
load_groups
# Load Prowler Checks
get_checks
# Clean up any temp files when prowler quits unexpectedly
trap clean_up EXIT
@@ -284,583 +291,55 @@ trap handle_ctrl_c INT
# Environment variable takes precedence over command line
unset AWS_DEFAULT_OUTPUT
. $PROWLER_DIR/include/colors
. $PROWLER_DIR/include/os_detector
. $PROWLER_DIR/include/aws_profile_loader
. $PROWLER_DIR/include/awscli_detector
. $PROWLER_DIR/include/whoami
. $PROWLER_DIR/include/assume_role
. $PROWLER_DIR/include/csv_header
. $PROWLER_DIR/include/banner
. $PROWLER_DIR/include/html_report
. $PROWLER_DIR/include/outputs_bucket
. $PROWLER_DIR/include/outputs
. $PROWLER_DIR/include/credentials_report
. $PROWLER_DIR/include/scoring
. $PROWLER_DIR/include/python_detector
. $PROWLER_DIR/include/secrets_detector
. $PROWLER_DIR/include/check_creds_last_used
. $PROWLER_DIR/include/check3x
. $PROWLER_DIR/include/connection_tests
. $PROWLER_DIR/include/securityhub_integration
. $PROWLER_DIR/include/junit_integration
. $PROWLER_DIR/include/organizations_metadata
. $PROWLER_DIR/include/custom_checks
. $PROWLER_DIR/include/allowlist
. $PROWLER_DIR/include/db_connector
# Check OS
os_detector
TIMESTAMP=$(get_iso8601_timestamp)
# Validate command line options that not requires AWS
validate_list_checks
validate_list_groups
validate_database
validate_modes
validate_output_bucket
#Parsing input options
# Parses the check file into CHECK_ID's.
if [[ -n "$CHECK_FILE" ]]; then
if [[ -f $CHECK_FILE ]]; then
# Parses the file, converting it to a comma seperated list. Ignores all # comments and removes extra blank spaces
CHECK_ID="$(awk '!/^[[:space:]]*#/{print }' <(cat $CHECK_FILE | sed 's/[[:space:]]*#.*$//g;/^$/d' | sed 'H;1h;$!d;x;y/\n/,/' | tr -d ' '))"
else
# If the file doesn't exist, exits Prowler
echo "$CHECK_FILE does not exist"
EXITCODE=1
exit $EXITCODE
fi
fi
# Check database providers
if [[ ${DATABASE_PROVIDER} ]]
then
# Check if input provider is supported
if ! grep -w -q -E "${DATABASE_PROVIDER}" <<< "${SUPPORTED_DB_PROVIDERS}"
then
db_exit_abnormally "${DATABASE_PROVIDER}" "DB provider not supported, providers supported: ${SUPPORTED_DB_PROVIDERS} - EXITING!"
elif ! command -v "psql" > /dev/null 2>&1
then
db_exit_abnormally "postgresql" "psql tool not installed or not found- EXITING!"
elif [[ $(find "${HOME}/.pgpass" -perm 600 2>/dev/null) != "$HOME/.pgpass" ]]
then
db_exit_abnormally "postgresql" ".pgpass file not found at $HOME/.pgpass or .pgpass file permissions not matching 600 - EXITING!"
else
IFS=':' read -r PSQL_HOSTNAME PSQL_PORT PSQL_DATABASE PSQL_USER PSQL_PASSWORD PSQL_TABLE < "$HOME/.pgpass"
if [[ ! ${PSQL_HOSTNAME} ]] || [[ ! ${PSQL_PORT} ]] || [[ ! ${PSQL_DATABASE} ]] || [[ ! ${PSQL_USER} ]] || [[ ! ${PSQL_PASSWORD} ]] || [[ ! ${PSQL_TABLE} ]]
then
db_exit_abnormally "postgresql" "Empty field into ${HOME}/.pgpass file, all fields must be filled. Please check Prowler README about .pgpass file format - EXITING!"
fi
export PSQL_USER
export PSQL_HOSTNAME
export PSQL_TABLE
# Once all the variables are defined and exported test if database instance is reachable
if ! pg_isready -q -U "${PSQL_USER}" -h "${PSQL_HOSTNAME}" -p "${PSQL_PORT}"
then
db_exit_abnormally "postgresql" "Database listening on host ${PSQL_HOSTNAME} on port ${PSQL_PORT} connected as user ${PSQL_USER} is unreachable - EXITING!"
# If database instance is ready time to check credentials
elif ! PGPASSWORD="${PSQL_PASSWORD}" psql -U "${PSQL_USER}" -h "${PSQL_HOSTNAME}" -c "\q" > /dev/null 2>&1
then
db_exit_abnormally "postgresql" "User ${PSQL_USER} invalid or invalid credentials, please check ${HOME}/.pgpass file - EXITING!"
# If credentials are ok -> database exists?
elif ! PGPASSWORD="${PSQL_PASSWORD}" psql -U "${PSQL_USER}" -h "${PSQL_HOSTNAME}" "${PSQL_DATABASE}" -c "\q" > /dev/null 2>&1
then
db_exit_abnormally "postgresql" "Database not exists, please check ${HOME}/.pgpass file - EXITING!"
# and finally, if database exists -> table exists ?
elif ! PGPASSWORD="${PSQL_PASSWORD}" psql -U "${PSQL_USER}" -h "${PSQL_HOSTNAME}" "${PSQL_DATABASE}" -c "SELECT * FROM ${PSQL_TABLE};" > /dev/null 2>&1
then
db_exit_abnormally "postgresql" "Table ${PSQL_TABLE} not exists, please check ${HOME}/.pgpass file - EXITING!"
fi
fi
fi
# Check modes
if [ -n "${MODE}" ]
then
IFS=',' read -ra MODES <<< "${MODE}"
for MODE in "${MODES[@]}"; do
if [[ "$MODE" != "mono" && "$MODE" != "text" && "$MODE" != "csv" && "$MODE" != "json" && "$MODE" != "json-asff" && "$MODE" != "junit-xml" && "$MODE" != "html" ]]; then
echo -e "${OPTRED}ERROR!$OPTNORMAL Invalid output mode. Choose text, mono, csv, json, json-asff, junit-xml or html. ./prowler -h for help"
EXITCODE=1
exit $EXITCODE
fi
done
fi
#Check output dir custom
if [ -n "${OUTPUT_DIR_CUSTOM}" ]
then
# If dir custom, mode has to be enabled (if you provide a custom directory it's because you want to store something)
if [ "${MODE}" != 'text' ]
then
# Check if custom folder exists
if [ ! -d "${OUTPUT_DIR_CUSTOM}" ]
then
echo "$OPTRED ERROR!$OPTNORMAL directory \"$OUTPUT_DIR_CUSTOM\" does not exist."
exit 1
else
OUTPUT_DIR=$OUTPUT_DIR_CUSTOM
fi
else
echo "$OPTRED ERROR!$OPTNORMAL - When using custom directory output Mode (-M) has to be set as well. Use -h for help."
exit 1
fi
# If custom output dir is not provided output dir -> default
else
OUTPUT_DIR="${PROWLER_DIR}/output"
if [ ! -d "${OUTPUT_DIR}" ]
then
mkdir -p "${OUTPUT_DIR}"
fi
fi
# End of input parameters tests
# Pre-process allowlist file if supplied
if [[ -n "$ALLOWLIST_FILE" ]]; then
allowlist
fi
# Create output file name
if [ -n "${OUTPUT_FILE_NAME}" ]
then
OUTPUT_FILE_NAME="${OUTPUT_DIR}/$OUTPUT_FILE_NAME"
else
OUTPUT_FILE_NAME="${OUTPUT_DIR}/prowler-output-${ACCOUNT_NUM}-${OUTPUT_DATE}"
fi
# Add headers to certain output files
output_files_init
# Load all of the groups of checks inside groups folder named as "groupNumber*"
for group in $(ls $PROWLER_DIR/groups/group[0-9]*|grep -v groupN_sample); do
. "$group"
done
# Load all of the checks inside checks folder named as "check*"
# this includes also extra checks since they are "check_extraNN"
for checks in $(ls $PROWLER_DIR/checks/check*|grep -v check_sample); do
. "$checks"
done
# include checks if external folder is specified
if [[ $EXTERNAL_CHECKS_PATH ]]; then
custom_checks
fi
# Get a list of total checks available by ID
for i in "${!GROUP_TITLE[@]}"; do
IFS=',' read -ra CHECKS <<< "${GROUP_CHECKS[$i]}"
for j in "${CHECKS[@]}"; do
TOTAL_CHECKS+=("$CHECK_ID_$j")
done
done
# Remove duplicates, sort checks numerically, and store the result as an array
# Note: the sort mechanism relies on the fact that the check ID prefixes 'check' and 'extra' are both 5 characters long.
# 6th character is the section number, 7th character onwards is the individual ID (e.g. check110 = check 1 10)
TOTAL_CHECKS=($(echo "${TOTAL_CHECKS[*]}" | tr ' ' '\n' | awk '!seen[$0]++' | sort -k 1.6,1.6n -k 1.7n))
# Function to get all regions
get_regions() {
# Get list of regions based on include/whoami
REGIONS=$($AWSCLI ec2 describe-regions --query 'Regions[].RegionName' --output text $PROFILE_OPT --region $REGION_FOR_STS --region-names ${FILTERREGION//[,]/ } 2>&1)
ret=$?
if [[ $ret -ne 0 ]]; then
echo "$OPTRED Access Denied trying to describe regions! Review permissions as described here: https://github.com/prowler-cloud/prowler/#requirements-and-installation $OPTNORMAL"
EXITCODE=1
exit $EXITCODE
fi
}
# Function to show the title of the check, and optionally which group(s) it belongs to
# using this way instead of arrays to keep bash3 (osx) and bash4(linux) compatibility
show_check_title() {
local check_id=CHECK_ID_$1
local check_title=CHECK_TITLE_$1
local check_scored=CHECK_SCORED_$1
local check_cis_level=CHECK_CIS_LEVEL_$1
local check_asff_compliance_type=CHECK_ASFF_COMPLIANCE_TYPE_$1
local check_severity=CHECK_SEVERITY_$1
local check_servicename=CHECK_SERVICENAME_$1
local group_ids
local group_index
local check_name
# If requested ($2 is any non-null value) iterate all GROUP_CHECKS and produce a comma-separated list of all
# the GROUP_IDs that include this particular check
if [[ -n "$2" ]]; then
for group_index in "${!GROUP_ID[@]}"; do
for check_name in $(echo "${GROUP_CHECKS[$group_index]}" | sed "s/,/ /g");do
if [[ "$check_name" == "$1" ]]; then
if [[ -n "$group_ids" ]]; then
group_ids+=", "
fi
group_ids+="${GROUP_ID[$group_index]}"
fi
done
done
fi
# This shows ASFF_COMPLIANCE_TYPE if group used is ens, this si used to show ENS compliance ID control, can be used for other compliance groups as well.
if [[ ${GROUP_ID_READ} == "ens" ]];then
textTitle "${!check_id}" "${!check_title}" "${!check_scored}" "${!check_cis_level}" "$group_ids" "(${!check_asff_compliance_type})"
else
textTitle "${!check_id}" "${!check_title}" "${!check_servicename}" "${!check_severity}" "$group_ids" "${!check_cis_level}"
fi
}
# Function to show the title of a group, by numeric id
show_group_title() {
# when csv mode is used, no group title is shown
if [[ "$MODE" != "csv" ]]; then
textTitle "${GROUP_NUMBER[$1]}" "${GROUP_TITLE[$1]}"
fi
}
# Function to execute the check
execute_check() {
if [[ -n "${ACCOUNT_TO_ASSUME}" || -n "${ROLE_TO_ASSUME}" ]]; then
# echo ******* I am here again to check on my role *******
# Following logic looks for time remaining in the session and review it
# if it is less than 600 seconds, 10 minutes.
CURRENT_TIMESTAMP=$(date -u "+%s")
SESSION_TIME_REMAINING=$(expr $AWS_SESSION_EXPIRATION - $CURRENT_TIMESTAMP)
# echo SESSION TIME REMAINING IN SECONDS: $SESSION_TIME_REMAINING
MINIMUM_REMAINING_TIME_ALLOWED=600
if (( $MINIMUM_REMAINING_TIME_ALLOWED > $SESSION_TIME_REMAINING )); then
# echo LESS THAN 10 MIN LEFT: RE-ASSUMING...
unset AWS_ACCESS_KEY_ID
unset AWS_SECRET_ACCESS_KEY
unset AWS_SESSION_TOKEN
assume_role
fi
fi
CHECK_ID="$1"
# See if this is an alternate name for a check
# for example, we might have been passed 1.01 which is another name for 1.1
local alternate_name_var=CHECK_ALTERNATE_$1
local alternate_name=${!alternate_name_var}
# See if this check defines an ASFF Type, if so, use this, falling back to a sane default
# For a list of Types Taxonomy, see: https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-findings-format-type-taxonomy.html
local asff_type_var=CHECK_ASFF_TYPE_$1
CHECK_ASFF_TYPE="${!asff_type_var:-Software and Configuration Checks}"
local asff_compliance_type_var=CHECK_ASFF_COMPLIANCE_TYPE_$1
CHECK_ASFF_COMPLIANCE_TYPE="${!asff_compliance_type_var:-Software and Configuration Checks}"
# See if this check defines an ASFF Resource Type, if so, use this, falling back to a sane default
# For a list of Resource Types, see: https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-findings-format.html#asff-resources
local asff_resource_type_var=CHECK_ASFF_RESOURCE_TYPE_$1
CHECK_ASFF_RESOURCE_TYPE="${!asff_resource_type_var:-AwsAccount}"
local severity_var=CHECK_SEVERITY_$1
CHECK_SEVERITY="${!severity_var}"
local servicename_var=CHECK_SERVICENAME_$1
CHECK_SERVICENAME="${!servicename_var}"
local risk_var=CHECK_RISK_$1
CHECK_RISK="${!risk_var}"
local remediation_var=CHECK_REMEDIATION_$1
CHECK_REMEDIATION="${!remediation_var}"
local doc_var=CHECK_DOC_$1
CHECK_DOC="${!doc_var}"
local caf_epic_var=CHECK_CAF_EPIC_$1
CHECK_CAF_EPIC="${!caf_epic_var}"
SECURITYHUB_NEW_FINDINGS_IDS=()
# Generate the credential report, only if it is group1 related which checks we
# run so that the checks can safely assume it's available
# set the custom ignores list for this check
ignores="$(awk "/${1}/{print}" <(echo "${ALLOWLIST}"))"
if [ ${alternate_name} ];then
if [[ ${alternate_name} == check1* || ${alternate_name} == extra71 || ${alternate_name} == extra774 || ${alternate_name} == extra7123 ]];then
if [ ! -s $TEMP_REPORT_FILE ];then
genCredReport
saveReport
fi
fi
show_check_title ${alternate_name}
if is_junit_output_enabled; then
prepare_junit_check_output "$1"
fi
# Execute the check
IGNORES="${ignores}" CHECK_NAME="$1" ${alternate_name}
if is_junit_output_enabled; then
finalise_junit_check_output "$1"
fi
if [[ "$SEND_TO_SECURITY_HUB" -eq 1 ]]; then
resolveSecurityHubPreviousFails "$1"
fi
else
# Check to see if this is a real check
local check_id_var=CHECK_ID_$1
local check_id=${!check_id_var}
if [ ${check_id} ]; then
if [[ ${check_id} == 1* || ${check_id} == 7.1 || ${check_id} == 7.74 || ${check_id} == 7.123 ]];then
if [ ! -s $TEMP_REPORT_FILE ];then
genCredReport
saveReport
fi
fi
show_check_title "$1"
if is_junit_output_enabled; then
prepare_junit_check_output "$1"
fi
# Execute the check
IGNORES="${ignores}" CHECK_NAME="$1" $1
if is_junit_output_enabled; then
finalise_junit_check_output "$1"
fi
if [[ "$SEND_TO_SECURITY_HUB" -eq 1 ]]; then
resolveSecurityHubPreviousFails "$1"
fi
else
textFail "Check ${CHECK_ID} does not exist. Use a valid check name (i.e. check41 or extra71)";
exit $EXITCODE
fi
fi
}
# Function to execute all checks in a group
execute_group() {
show_group_title $1
# run the checks in the group
IFS=',' read -ra CHECKS <<< ${GROUP_CHECKS[$1]}
# Exclude any checks specified
if [[ -n ${2} ]]; then
EXCLUDED_CHECKS=()
NEW_CHECKS=()
IFS=',' read -ra EXCLUDED_CHECKS <<< "${2},"
for exc in ${EXCLUDED_CHECKS[@]} ; do
for i in ${!CHECKS[@]} ; do
[[ ${CHECKS[i]} = ${exc} ]] && unset CHECKS[i]
done
done
unset EXCLUDED_CHECKS
fi
for i in ${CHECKS[@]}; do
execute_check ${i}
done
}
# Function to execute group by name
execute_group_by_id() {
for i in "${!GROUP_ID[@]}"; do
if [ "${GROUP_ID[$i]}" == "$1" ]; then
execute_group ${i} $2
fi
done
}
# Function to execute all checks in all groups except extras if -e is invoked
execute_all() {
for i in "${!GROUP_TITLE[@]}"; do
if [[ $EXTRAS ]]; then
GROUP_RUN_BY_DEFAULT[7]='N'
fi
if [ "${GROUP_RUN_BY_DEFAULT[$i]}" == "Y" ]; then
execute_group $i
fi
done
}
# Function to show the titles of either all checks or only those in the specified group
show_all_titles() {
local checks
local check_id
local group_index
# If '-g <group_id>' has been specified, only show the titles of checks within the specified group
if [[ $GROUP_ID_READ ]];then
if [[ " ${GROUP_ID[@]} " =~ " ${GROUP_ID_READ} " ]]; then
for group_index in "${!GROUP_ID[@]}"; do
if [ "${GROUP_ID[$group_index]}" == "${GROUP_ID_READ}" ]; then
show_group_title "$group_index"
IFS=',' read -ra checks <<< "${GROUP_CHECKS[$group_index]}"
for check_id in ${checks[@]}; do
show_check_title "$check_id"
done
fi
done
else
textFail "Group ${GROUP_ID_READ} does not exist. Use a valid check group ID i.e.: group1, extras, forensics-ready, etc."
show_all_group_titles
exit $EXITCODE
fi
else
for check_id in "${TOTAL_CHECKS[@]}"; do
# Pass 1 so that the group IDs that this check belongs to are printed
show_check_title "$check_id" 1
done
fi
}
show_all_group_titles() {
local group_index
for group_index in "${!GROUP_TITLE[@]}"; do
show_group_title "$group_index"
done
}
# Function to execute all checks but exclude some of them
get_all_checks_without_exclusion() {
CHECKS_EXCLUDED=()
local CHECKS_TO_EXCLUDE=()
# Get a list of checks to exclude
IFS=',' read -ra E_CHECKS <<< "$1"
for E_CHECK in "${E_CHECKS[@]}"; do
CHECKS_TO_EXCLUDE+=($E_CHECK)
done
# Create a list that contains all checks but excluded ones
for i in "${TOTAL_CHECKS[@]}"; do
local COINCIDENCE=false
for x in "${CHECKS_TO_EXCLUDE[@]}"; do
if [[ "$i" == "$x" ]]; then
COINCIDENCE=true
fi
done
if [[ "$COINCIDENCE" = false ]]; then
CHECKS_EXCLUDED+=($i)
fi
done
}
### All functions defined above ... run the workflow
#if [[ " ${MODES[@]} " =~ " mono " || " ${MODES[@]} " =~ " text " ]]; then
prowlerBanner
#fi
# List only check tittles
if [[ $PRINTCHECKSONLY == "1" ]]; then
show_all_titles
exit $EXITCODE
fi
# List only group tittles
if [[ $PRINTGROUPSONLY == "1" ]]; then
show_all_group_titles
exit $EXITCODE
fi
# Check that jq is installed for JSON outputs
if [[ " ${MODES[@]} " =~ " json " || " ${MODES[@]} " =~ " json-asff " ]]; then
. $PROWLER_DIR/include/jq_detector
fi
if [[ "$SEND_TO_SECURITY_HUB" -eq 1 ]]; then
checkSecurityHubCompatibility
fi
if is_junit_output_enabled; then
prepare_junit_output
fi
# First, check AWS Organizations Metadata
if [[ -n "${MANAGEMENT_ACCOUNT_ID}" && -n "${ROLE_TO_ASSUME}" ]]
then
# Backing up initial credentials
backupInitialAWSCredentials
# Backing up initial account to assume
INITIAL_ACCOUNT_TO_ASSUME="${ACCOUNT_TO_ASSUME}"
# Set the new account to assume to recover AWS Organizations Metadata
ACCOUNT_TO_ASSUME="${MANAGEMENT_ACCOUNT_ID}"
# Recover AWS Organizations Metadata
get_orgs_account_details
# Restoring account to assume to -A field after getting account metadata
ACCOUNT_TO_ASSUME="${INITIAL_ACCOUNT_TO_ASSUME}"
# Restoring initial credentials
restoreInitialAWSCredentials
fi
# Check AWS CLI
aws_cli_detector
# load AWS profile
aws_profile_loader
# Gather account data / test aws cli connectivity
getWhoami
if [[ -n "${ACCOUNT_TO_ASSUME}" || -n "${ROLE_TO_ASSUME}" ]]; then
backupInitialAWSCredentials
assume_role
fi
get_aws_credentials
print_whoami
# List regions
get_regions
# Execute group of checks if called with -g
if [[ $GROUP_ID_READ ]];then
if [[ " ${GROUP_ID[@]} " =~ " ${GROUP_ID_READ} " ]]; then
# Validate command line options that requires AWS
validate_custom_output_directory
validate_custom_output_filename
validate_security_hub
validate_organizations
validate_allowlist
validate_custom_checks
execute_group_by_id ${GROUP_ID_READ} ${EXCLUDE_CHECK_ID}
if [[ "${MODES[@]}" =~ "html" ]]; then
addHtmlFooter >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
fi
cleanTemp
scoring
if [[ $OUTPUT_BUCKET_NOASSUME ]]; then
restoreInitialAWSCredentials
fi
if [[ $OUTPUT_BUCKET ]]; then
copyToS3
fi
exit $EXITCODE
else
textFail "Group ${GROUP_ID_READ} does not exist. Use a valid check group ID i.e.: group1, extras, forensics-ready, etc."
show_all_group_titles
exit $EXITCODE
fi
fi
# Add headers to certain output files
output_files_init
# Get a list of total checks excluding a list provided by the user and overwrite CHECK_ID with the result
# if the list provided by the user contains an invalid check, this will be discarded.
# if the list provided by the user contains just one argument and is invalid, then it will be discarded and all tests will be executed
if [[ ${EXCLUDE_CHECK_ID} ]];then
get_all_checks_without_exclusion ${EXCLUDE_CHECK_ID}
function join { local IFS="$1"; shift; echo "$*"; }
CHECKS_EXCLUDED=$(join , "${CHECKS_EXCLUDED[@]}")
CHECK_ID=${CHECKS_EXCLUDED}
fi
# Execute checks
for CHECK in "${TOTAL_CHECKS[@]}"
do
execute_check "${CHECK}"
done
# Execute single check if called with -c
if [[ $CHECK_ID ]];then
IFS=',' read -ra CHECKS <<< "$CHECK_ID"
for CHECK in "${CHECKS[@]}"; do
execute_check $CHECK
done
if [[ "${MODES[@]}" =~ "html" ]]; then
addHtmlFooter >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
fi
if [[ $OUTPUT_BUCKET_NOASSUME ]]; then
restoreInitialAWSCredentials
fi
if [[ $OUTPUT_BUCKET ]]; then
copyToS3
fi
scoring
cleanTemp
exit $EXITCODE
fi
# Add bottom to certain output files
output_files_end
execute_all
if [[ "${MODES[@]}" =~ "html" ]]; then
addHtmlFooter >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
fi
# Validate output bucket without assuming role
validate_copy_to_s3_no_assume
# Copy Prowler Findings to S3 if required
copy_to_s3_assume
# Score if required
scoring
cleanTemp
if [[ $OUTPUT_BUCKET_NOASSUME ]]; then
restoreInitialAWSCredentials
fi
if [[ $OUTPUT_BUCKET ]]; then
copyToS3
fi
exit $EXITCODE