mirror of
https://github.com/ghndrx/prowler.git
synced 2026-02-10 23:05:05 +00:00
New folder structure phase 1
This commit is contained in:
82
lib/common/allowlist
Normal file
82
lib/common/allowlist
Normal file
@@ -0,0 +1,82 @@
|
||||
#!/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.
|
||||
|
||||
allowlist(){
|
||||
# Check if the file is an S3 URI
|
||||
if grep -q -E "^s3://([^/]+)/(.*?([^/]+))$" <<< "${ALLOWLIST_FILE}"; then
|
||||
allowlist_S3
|
||||
# Check if the file is a DynamoDB ARN
|
||||
elif grep -q -E "^arn:[aws\|aws\-cn\|aws\-us\-gov]+:dynamodb:[a-z]{2}-[north\|south\|east\|west\|central]+-[1-9]{1}:[0-9]{12}:table\/[a-zA-Z0-9._-]+$" <<< "${ALLOWLIST_FILE}"; then
|
||||
allowlist_DynamoDB
|
||||
else
|
||||
# Check if the file is a DynamoDB ARN
|
||||
allowlist_Textfile
|
||||
fi
|
||||
}
|
||||
|
||||
allowlist_S3() {
|
||||
# download s3 object
|
||||
local S3_ALLOWLIST_FILE=allowlist_s3_file.txt
|
||||
echo -e "${NOTICE} Downloading allowlist from S3 URI ${ALLOWLIST_FILE} ...${NORMAL}"
|
||||
if ! "${AWSCLI}" s3 cp "${ALLOWLIST_FILE}" "${S3_ALLOWLIST_FILE}" ${PROFILE_OPT} > /dev/null 2>&1; then
|
||||
echo "${BAD} FAIL! Access Denied trying to download allowlist from the S3 URI, please make sure it is correct and/or you have permissions to get the S3 object.${NORMAL}"
|
||||
EXITCODE=1
|
||||
exit "${EXITCODE}"
|
||||
fi
|
||||
echo -e "${OK} Success! Allowlist was downloaded, starting Prowler...${NORMAL}"
|
||||
# ignore lines starting with # (comments)
|
||||
# ignore inline comments: check1:foo # inline comment
|
||||
ALLOWLIST=$(awk '!/^[[:space:]]*#/{print }' <(cat "${S3_ALLOWLIST_FILE}") | sed 's/[[:space:]]*#.*$//g')
|
||||
# remove temporary file
|
||||
rm -f "${S3_ALLOWLIST_FILE}"
|
||||
}
|
||||
|
||||
allowlist_DynamoDB() {
|
||||
echo -e "${NOTICE} Getting allowlist from DynamoDB table ${ALLOWLIST_FILE} ...${NORMAL}"
|
||||
DYNAMO_REGION=$(cut -d ":" -f 4 <<< "${ALLOWLIST_FILE}")
|
||||
DYNAMO_TABLE=$(cut -d "/" -f 2 <<< "${ALLOWLIST_FILE}")
|
||||
DYNAMO_ITEMS=$(${AWSCLI} dynamodb execute-statement --statement "SELECT rule FROM \"${DYNAMO_TABLE}\" WHERE account_id=""'""${ACCOUNT_NUM}""'"" or account_id='*'" ${PROFILE_OPT} --region ${DYNAMO_REGION} --output json 2>&1 )
|
||||
if grep -q -E 'AccessDenied|UnauthorizedOperation|ResourceNotFoundException' <<< "${DYNAMO_ITEMS}"; then
|
||||
echo "${BAD} FAIL! Access Denied trying to get allowlist from the DynamoDB, please make sure it is correct and/or you have permissions to scan the table ${DYNAMO_TABLE}.${NORMAL}"
|
||||
EXITCODE=1
|
||||
exit ${EXITCODE}
|
||||
fi
|
||||
if [[ $(jq '."Items" | length' <<< "${DYNAMO_ITEMS}") -eq 0 ]]; then
|
||||
echo "${NOTICE} No allowed resources were found for account ${ACCOUNT_NUM}, starting Prowler...${NORMAL}"
|
||||
else
|
||||
# Convert elements to allowlist file
|
||||
ALLOWLIST=$(jq -r '.Items[].rule.S' <<< "${DYNAMO_ITEMS}")
|
||||
if grep -q "null" <<< "${ALLOWLIST}"; then
|
||||
echo "${BAD} FAIL! No rule key found in DynamoDB table, please make sure the table has rule as a sort key and account_id as the partition key...${NORMAL}"
|
||||
EXITCODE=1
|
||||
exit ${EXITCODE}
|
||||
else
|
||||
echo "${OK} Success! Allowed resources were added for account ${ACCOUNT_NUM}, starting Prowler...${NORMAL}"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
allowlist_Textfile() {
|
||||
echo -e "${NOTICE} Getting allowlist from input file ${ALLOWLIST_FILE} ...${NORMAL}"
|
||||
# Check if input allowlist file exists
|
||||
if [[ -f "${ALLOWLIST_FILE}" ]]; then
|
||||
# ignore lines starting with # (comments)
|
||||
# ignore inline comments: check1:foo # inline comment
|
||||
ALLOWLIST=$(awk '!/^[[:space:]]*#/{print }' <(cat "${ALLOWLIST_FILE}") | sed 's/[[:space:]]*#.*$//g')
|
||||
echo -e "${OK} Success! Allowlist was downloaded, starting Prowler...${NORMAL}"
|
||||
else
|
||||
echo "${BAD} FAIL! ${ALLOWLIST_FILE} does not exist, please input a valid allowlist file.${NORMAL}"
|
||||
EXITCODE=1
|
||||
exit ${EXITCODE}
|
||||
fi
|
||||
}
|
||||
35
lib/common/banner
Normal file
35
lib/common/banner
Normal file
@@ -0,0 +1,35 @@
|
||||
#!/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.
|
||||
|
||||
prowlerBanner() {
|
||||
if [[ $BANNER != "0" ]]; then
|
||||
echo -e "$CYAN _"
|
||||
echo -e " _ __ _ __ _____ _| | ___ _ __"
|
||||
echo -e " | '_ \| '__/ _ \ \ /\ / / |/ _ \ '__|"
|
||||
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)"
|
||||
printColorsCode
|
||||
fi
|
||||
}
|
||||
|
||||
infoReferenceLong(){
|
||||
# Report review note:
|
||||
echo -e ""
|
||||
echo -e "For more information on the Prowler, feedback and issue reporting:"
|
||||
echo -e "https://github.com/prowler-cloud/prowler"
|
||||
echo -e ""
|
||||
echo -e "For more information on the CIS benchmark:"
|
||||
echo -e "https://benchmarks.cisecurity.org/tools2/amazon/CIS_Amazon_Web_Services_Foundations_Benchmark_v1.1.0.pdf"
|
||||
}
|
||||
143
lib/common/check_creds_last_used
Normal file
143
lib/common/check_creds_last_used
Normal file
@@ -0,0 +1,143 @@
|
||||
#!/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.
|
||||
|
||||
# Set of functions for checking credential usage, following CIS 1.3 "Ensure credentials unused for 90 days or greater are disabled" rules
|
||||
# but support a custom time-range to allow for stricter policies, e.g. extra774
|
||||
|
||||
# CSV Report Column Numbering
|
||||
# See also https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_getting-report.html#id_credentials_understanding_the_report_format
|
||||
# 1 - user
|
||||
# 2 - arn
|
||||
# 3 - user_creation_time
|
||||
# 4 - password_enabled
|
||||
# 5 - password_last_used
|
||||
# 6 - password_last_changed
|
||||
# 7 - password_next_rotation
|
||||
# 8 - mfa_active
|
||||
# 9 - access_key_1_active
|
||||
# 10 - access_key_1_last_rotated
|
||||
# 11 - access_key_1_last_used_date
|
||||
# 12 - access_key_1_last_used_region
|
||||
# 13 - access_key_1_last_used_service
|
||||
# 14 - access_key_2_active
|
||||
# 15 - access_key_2_last_rotated
|
||||
# 16 - access_key_2_last_used_date
|
||||
# 17 - access_key_2_last_used_region
|
||||
# 18 - access_key_2_last_used_service
|
||||
# 19 - cert_1_active
|
||||
# 20 - cert_1_last_rotated
|
||||
# 21 - cert_2_active
|
||||
# 22 - cert_2_last_rotated
|
||||
|
||||
# Check both passwords and access keys - e.g. CIS rule
|
||||
check_creds_used_in_last_days() {
|
||||
local max_days=$1
|
||||
|
||||
check_passwords_used_in_last_days "$max_days"
|
||||
check_access_keys_used_in_last_days "$max_days"
|
||||
}
|
||||
|
||||
check_passwords_used_in_last_days() {
|
||||
local max_days=$1
|
||||
|
||||
local user
|
||||
local users_with_password_enabled
|
||||
local last_login_date
|
||||
local days_since_password_last_changed
|
||||
local days_password_not_in_use
|
||||
users_with_password_enabled=$(awk -F, '{ print $1,$4 }' "$TEMP_REPORT_FILE" | grep " true$" | awk '{ print $1 }')
|
||||
# Only check password last used date for users with password enabled
|
||||
if [[ $users_with_password_enabled ]]; then
|
||||
for user in $users_with_password_enabled; do
|
||||
last_login_date=$(awk -F, '{ print $1,$5 }' "$TEMP_REPORT_FILE" | grep "^$user " | awk '{ print $2 }')
|
||||
|
||||
# If the user has never logged into the console, their last login date is 'no_information'. See:
|
||||
# https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_getting-report.html#id_credentials_understanding_the_report_format
|
||||
if [[ "${last_login_date}" == "no_information" ]]; then
|
||||
user_password_changed_date=$(awk -F, '{ print $1,$6 }' "$TEMP_REPORT_FILE" | grep "^$user " | awk '{ print $2 }')
|
||||
days_since_password_last_changed=$(how_older_from_today "${user_password_changed_date%T*}")
|
||||
|
||||
# "When password_enabled is set to TRUE and password_last_used is set to no_information, ensure password_last_changed is less than X days ago"
|
||||
if [[ "$days_since_password_last_changed" -ge "$max_days" ]]; then
|
||||
textFail "$REGION: User $user has never logged into the console since creation and their password not changed in the past ${max_days} days" "$REGION" "$user"
|
||||
else
|
||||
textInfo "$REGION: User $user has not logged into the console since creation" "$REGION" "$user"
|
||||
fi
|
||||
else
|
||||
days_password_not_in_use=$(how_older_from_today "${last_login_date%T*}")
|
||||
|
||||
# "For each user having password_enabled set to TRUE, ensure password_last_used_date is less than X days ago."
|
||||
if [[ "$days_password_not_in_use" -ge "$max_days" ]]; then
|
||||
textFail "$REGION: User $user has not logged into the console in the past ${max_days} days" "$REGION" "$user"
|
||||
else
|
||||
textPass "$REGION: User $user has logged into the console in the past ${max_days} days" "$REGION" "$user"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
else
|
||||
textPass "$REGION: No users found with password enabled" "$REGION" "$user"
|
||||
fi
|
||||
}
|
||||
|
||||
check_access_keys_used_in_last_days() {
|
||||
local max_days=$1
|
||||
|
||||
check_access_key_used_in_last_days "$max_days" 1 9 10 11
|
||||
check_access_key_used_in_last_days "$max_days" 2 14 15 16
|
||||
}
|
||||
|
||||
check_access_key_used_in_last_days() {
|
||||
local max_days=$1
|
||||
local access_key_name=$2
|
||||
local access_key_active_col=$3
|
||||
local access_key_last_rotated_col=$4
|
||||
local access_key_last_used_col=$5
|
||||
|
||||
local user
|
||||
local users_with_access_key_enabled
|
||||
local access_key_last_used_date
|
||||
local access_key_last_rotated_date
|
||||
local days_since_access_key_rotated
|
||||
local days_since_access_key_used
|
||||
users_with_access_key_enabled=$(awk -F, -v i="$access_key_active_col" '{ print $1,$i }' "$TEMP_REPORT_FILE" | grep " true$" | awk '{ print $1 }')
|
||||
# Only check access key last used date for users with this access key enabled
|
||||
if [[ $users_with_access_key_enabled ]]; then
|
||||
for user in $users_with_access_key_enabled; do
|
||||
access_key_last_used_date=$(awk -F, -v i="$access_key_last_used_col" '{ print $1,$i }' "$TEMP_REPORT_FILE" | grep "^$user " | awk '{ print $2 }')
|
||||
|
||||
if [[ "${access_key_last_used_date}" == "N/A" ]]; then
|
||||
access_key_last_rotated_date=$(awk -F, -v i="$access_key_last_rotated_col" '{ print $1,$i }' "$TEMP_REPORT_FILE" | grep "^$user " | awk '{ print $2 }')
|
||||
days_since_access_key_rotated=$(how_older_from_today "${access_key_last_rotated_date%T*}")
|
||||
|
||||
# "When a user having an access_key_x_active (where x is 1 or 2) to TRUE and corresponding access_key_x_last_used_date is set to N/A,
|
||||
# ensure access_key_x_last_rotated is less than X days ago"
|
||||
if [[ "$days_since_access_key_rotated" -ge "$max_days" ]]; then
|
||||
textFail "$REGION: User $user has never used access key $access_key_name since creation and not rotated it in the past ${max_days} days" "$REGION" "$user"
|
||||
else
|
||||
textInfo "$REGION: User $user has not used access key $access_key_name since creation" "$REGION" "$user"
|
||||
fi
|
||||
else
|
||||
days_since_access_key_used=$(how_older_from_today "${access_key_last_used_date%T*}")
|
||||
|
||||
# "For each user having an access_key_1_active or access_key_2_active to TRUE, ensure the corresponding access_key_n_last_used_date is less than X days ago"
|
||||
if [[ "$days_since_access_key_used" -ge "$max_days" ]]; then
|
||||
textFail "$REGION: User $user has not used access key $access_key_name in the past ${max_days} days" "$REGION" "$user"
|
||||
else
|
||||
textPass "$REGION: User $user has used access key $access_key_name in the past ${max_days} days" "$REGION" "$user"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
else
|
||||
textPass "$REGION: No users found with access key $access_key_name enabled" "$REGION" "$user"
|
||||
fi
|
||||
}
|
||||
76
lib/common/colors
Normal file
76
lib/common/colors
Normal file
@@ -0,0 +1,76 @@
|
||||
#!/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.
|
||||
|
||||
|
||||
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
|
||||
if [[ "$MODE" == "mono" || "$MODE" == "csv" || "$MODE" == "json" || "$MODE" == "json-asff" ]]; then
|
||||
MONOCHROME=1
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $MONOCHROME -eq 1 ]]; then
|
||||
# Colors
|
||||
NORMAL=''
|
||||
WARNING='' # Bad (red)
|
||||
SECTION='' # Section (yellow)
|
||||
NOTICE='' # Notice (yellow)
|
||||
OK='' # Ok (green)
|
||||
BAD='' # Bad (red)
|
||||
CYAN=''
|
||||
BLUE=''
|
||||
BROWN=''
|
||||
DARKGRAY=''
|
||||
GRAY=''
|
||||
GREEN=''
|
||||
MAGENTA=''
|
||||
PURPLE=''
|
||||
RED=''
|
||||
YELLOW=''
|
||||
WHITE=''
|
||||
else
|
||||
# Colors
|
||||
# NOTE: Your editor may NOT show the 0x1b / escape character left of the '['
|
||||
NORMAL="[0;39m"
|
||||
WARNING="[0;33m" # Warning (brown)
|
||||
SECTION="[1;33m" # Section (yellow)
|
||||
NOTICE="[1;33m" # Notice (yellow)
|
||||
OK="[1;32m" # Ok (green)
|
||||
BAD="[1;31m" # Bad (red)
|
||||
CYAN="[0;36m"
|
||||
BLUE="[0;34m"
|
||||
BROWN="[0;33m"
|
||||
DARKGRAY="[0;30m"
|
||||
GRAY="[0;37m"
|
||||
GREEN="[1;32m"
|
||||
MAGENTA="[1;35m"
|
||||
PURPLE="[0;35m"
|
||||
RED="[1;31m"
|
||||
YELLOW="[1;33m"
|
||||
WHITE="[1;37m"
|
||||
fi
|
||||
|
||||
printColorsCode(){
|
||||
if [[ $MONOCHROME -eq 0 ]]; then
|
||||
echo -e "\n$NORMAL Color code for results: "
|
||||
echo -e " - $NOTICE INFO (Information)$NORMAL"
|
||||
echo -e " - $OK PASS (Recommended value)$NORMAL"
|
||||
echo -e " - $WARNING WARNING (Ignored by allowlist)$NORMAL"
|
||||
echo -e " - $BAD FAIL (Fix required)$NORMAL"
|
||||
fi
|
||||
}
|
||||
46
lib/common/connection_tests
Normal file
46
lib/common/connection_tests
Normal file
@@ -0,0 +1,46 @@
|
||||
#!/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.
|
||||
|
||||
|
||||
# Function test_tcp_connectivity is in include/os_detector
|
||||
|
||||
# see here https://gist.github.com/rsvp/1171304/3d6714a469105faf03943b685090f90f576cf904
|
||||
|
||||
# Functions to connection responses initially used for Elasticsearch related checks
|
||||
httpStatus(){
|
||||
case $1 in
|
||||
000) SERVER_RESPONSE="000 Not responding" ;;
|
||||
200) SERVER_RESPONSE="200 Successful" ;;
|
||||
300) SERVER_RESPONSE="300 Multiple Choices" ;;
|
||||
301) SERVER_RESPONSE="301 Moved Permanently" ;;
|
||||
302) SERVER_RESPONSE="302 Found residing temporarily under different URI" ;;
|
||||
303) SERVER_RESPONSE="303 See Other" ;;
|
||||
304) SERVER_RESPONSE="304 Not Modified" ;;
|
||||
305) SERVER_RESPONSE="305 Use Proxy" ;;
|
||||
306) SERVER_RESPONSE="306 Status not defined" ;;
|
||||
307) SERVER_RESPONSE="307 Temporary Redirect" ;;
|
||||
301) SERVER_RESPONSE="301 Moved" ;;
|
||||
400) SERVER_RESPONSE="400 Error: Bad Request" ;;
|
||||
401) SERVER_RESPONSE="401 Error: Unauthorized" ;;
|
||||
403) SERVER_RESPONSE="403 Error: Forbidden" ;;
|
||||
404) SERVER_RESPONSE="404 Error: Not Found" ;;
|
||||
407) SERVER_RESPONSE="407 Error: Proxy Authentication Required" ;;
|
||||
408) SERVER_RESPONSE="408 Error: Request Timeout" ;;
|
||||
500) SERVER_RESPONSE="500 Error: Internal Server Error" ;;
|
||||
502) SERVER_RESPONSE="502 Error: Bad Gateway" ;;
|
||||
503) SERVER_RESPONSE="503 Error: Service Unavailable" ;;
|
||||
504) SERVER_RESPONSE="504 Error: Gateway Timeout" ;;
|
||||
505) SERVER_RESPONSE="505 Error: HTTP Version Not Supported" ;;
|
||||
*) SERVER_RESPONSE="HTTP: SERVER_RESPONSE not defined." ;;
|
||||
esac
|
||||
}
|
||||
17
lib/common/csv_header
Normal file
17
lib/common/csv_header
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/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.
|
||||
|
||||
|
||||
printCsvHeader() {
|
||||
echo "PROFILE${SEP}ACCOUNT_NUM${SEP}REGION${SEP}TITLE_ID${SEP}CHECK_RESULT${SEP}ITEM_SCORED${SEP}ITEM_LEVEL${SEP}TITLE_TEXT${SEP}CHECK_RESULT_EXTENDED${SEP}CHECK_ASFF_COMPLIANCE_TYPE${SEP}CHECK_SEVERITY${SEP}CHECK_SERVICENAME${SEP}CHECK_ASFF_RESOURCE_TYPE${SEP}CHECK_ASFF_TYPE${SEP}CHECK_RISK${SEP}CHECK_REMEDIATION${SEP}CHECK_DOC${SEP}CHECK_CAF_EPIC${SEP}CHECK_RESOURCE_ID${SEP}PROWLER_START_TIME${SEP}ACCOUNT_DETAILS_EMAIL${SEP}ACCOUNT_DETAILS_NAME${SEP}ACCOUNT_DETAILS_ARN${SEP}ACCOUNT_DETAILS_ORG${SEP}ACCOUNT_DETAILS_TAGS" >> ${OUTPUT_FILE_NAME}.$EXTENSION_CSV
|
||||
}
|
||||
52
lib/common/custom_checks
Normal file
52
lib/common/custom_checks
Normal file
@@ -0,0 +1,52 @@
|
||||
#!/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.
|
||||
|
||||
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
|
||||
# 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"
|
||||
mkdir "${S3_CHECKS_TEMP_FOLDER}"
|
||||
$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"
|
||||
done
|
||||
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"
|
||||
EXITCODE=1
|
||||
# remove temporary dir
|
||||
rm -rf "${S3_CHECKS_TEMP_FOLDER}"
|
||||
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"
|
||||
done
|
||||
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"
|
||||
EXITCODE=1
|
||||
exit $EXITCODE
|
||||
fi
|
||||
fi
|
||||
}
|
||||
216
lib/common/html_report
Normal file
216
lib/common/html_report
Normal file
@@ -0,0 +1,216 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2020) 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.
|
||||
|
||||
|
||||
|
||||
addHtmlHeader() {
|
||||
if [[ $PROFILE == "" ]];then
|
||||
PROFILE="ENV"
|
||||
fi
|
||||
if [[ -z $HTML_REPORT_INIT ]]; then
|
||||
cat <<EOF
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<!-- Required meta tags -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<style>
|
||||
.read-more {color:#00f;}
|
||||
.bg-success-custom {background-color: #70dc88 !important;}
|
||||
</style>
|
||||
<!-- Bootstrap CSS -->
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
|
||||
<!-- https://datatables.net/download/index with jQuery, DataTables, Buttons, SearchPanes, and Select //-->
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/dt/jqc-1.12.4/dt-1.10.25/b-1.7.1/sp-1.3.0/sl-1.3.3/datatables.min.css"/>
|
||||
<link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css" integrity="sha384-AYmEC3Yw5cVb3ZcuHtOA93w35dYTsvhLPVnYs9eStHfGJvOvKxVfELGroGkvsg+p" crossorigin="anonymous"/>
|
||||
<style>
|
||||
.show-read-more .more-text{
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<title>Prowler - AWS Security Assessments</title>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-xl sticky-top navbar-dark bg-dark">
|
||||
<a class="navbar-brand" href="#">Prowler - Security Assessments in AWS</a>
|
||||
</nav>
|
||||
<div class="container-fluid">
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
Report Information:
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item">
|
||||
<div class="row">
|
||||
<div class="col-md-auto">
|
||||
<b>Version:</b> $PROWLER_VERSION
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>Parameters used:</b> $PROWLER_PARAMETERS
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>Date:</b> $TIMESTAMP
|
||||
</li>
|
||||
<li class="list-group-item text-center">
|
||||
<a href="$HTML_LOGO_URL"><img src="$HTML_LOGO_IMG"
|
||||
alt="prowler-logo"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
Assessment Summary:
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item">
|
||||
<b>AWS Account:</b> $ACCOUNT_NUM
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>AWS-CLI Profile:</b> $PROFILE
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>API Region:</b> $REGION
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>User Id:</b> $USER_ID
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>Caller Identity ARN:</b> $CALLER_ARN
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
Scoring Information:
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item">
|
||||
<b>Prowler Score:</b> PROWLER_SCORE%
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>Total Resources:</b> TOTAL_RESOURCES
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>Passed:</b> PASS_COUNTER
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>Failed:</b> FAIL_COUNTER
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>Total Checks Executed:</b> CHECKS_COUNTER
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-12">
|
||||
<table class="table compact stripe row-border ordering" id="findingsTable" data-order='[[ 5, "asc" ]]' data-page-length='100'>
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th scope="col">Result</th>
|
||||
<th scope="col">Severity</th>
|
||||
<th scope="col">AccountID</th>
|
||||
<th scope="col">Region</th>
|
||||
<th scope="col">Compliance</th>
|
||||
<th scope="col">Service</th>
|
||||
<th scope="col">CheckID</th>
|
||||
<th style="width:20%" scope="col">Check Title</th>
|
||||
<th style="width:20%" scope="col">Check Output</th>
|
||||
<th scope="col">CIS Level</th>
|
||||
<th scope="col">CAF Epic</th>
|
||||
<th scope="col">Risk</th>
|
||||
<th scope="col">Remediation</th>
|
||||
<th scope="col">Docs</th>
|
||||
<th scope="col">Resource ID</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
EOF
|
||||
|
||||
fi
|
||||
}
|
||||
|
||||
addHtmlFooter() {
|
||||
cat <<EOF
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Table search and paginator -->
|
||||
<!-- Optional JavaScript -->
|
||||
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
|
||||
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.bundle.min.js" integrity="sha384-1CmrxMRARb6aLqgBO7yyAxTOQE2AKb9GfXnEo760AUcUmFx3ibVJJAzGytlQcNXd" crossorigin="anonymous"></script>
|
||||
<!-- https://datatables.net/download/index with jQuery, DataTables, Buttons, SearchPanes, and Select //-->
|
||||
<script type="text/javascript" src="https://cdn.datatables.net/v/dt/jqc-1.12.4/dt-1.10.25/b-1.7.1/sp-1.3.0/sl-1.3.3/datatables.min.js"></script>
|
||||
|
||||
<script>
|
||||
\$(document).ready(function(){
|
||||
|
||||
// Initialise the table with 50 rows, and some search/filtering panes
|
||||
\$('#findingsTable').DataTable( {
|
||||
lengthMenu: [ [50, 100, -1], [50, 100, "All"] ],
|
||||
searchPanes: {
|
||||
cascadePanes: true,
|
||||
viewTotal: true
|
||||
},
|
||||
dom: 'Plfrtip',
|
||||
columnDefs: [
|
||||
{
|
||||
searchPanes: {
|
||||
show: false
|
||||
},
|
||||
// Hide Compliance, Check ID (in favour of Check Title), CAF Epic, Risk, Remediation, Link
|
||||
targets: [4, 6, 9, 10, 11, 12]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
var maxLength = 30;
|
||||
\$(".show-read-more").each(function(){
|
||||
var myStr = \$(this).text();
|
||||
if(\$.trim(myStr).length > maxLength){
|
||||
var newStr = myStr.substring(0, maxLength);
|
||||
var removedStr = myStr.substring(maxLength, \$.trim(myStr).length);
|
||||
\$(this).empty().html(newStr);
|
||||
\$(this).append(' <a href="javascript:void(0);" class="read-more">read more...</a>');
|
||||
\$(this).append('<span class="more-text">' + removedStr + '</span>');
|
||||
}
|
||||
});
|
||||
\$(".read-more").click(function(){
|
||||
\$(this).siblings(".more-text").contents().unwrap();
|
||||
\$(this).remove();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
|
||||
unset HTML_REPORT_INIT
|
||||
}
|
||||
20
lib/common/jq_detector
Normal file
20
lib/common/jq_detector
Normal file
@@ -0,0 +1,20 @@
|
||||
#!/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.
|
||||
|
||||
# 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
|
||||
119
lib/common/junit_integration
Normal file
119
lib/common/junit_integration
Normal file
@@ -0,0 +1,119 @@
|
||||
#!/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.
|
||||
|
||||
# Generates JUnit XML reports which can be read by Jenkins or other CI tools
|
||||
|
||||
JUNIT_OUTPUT_DIRECTORY="junit-reports"
|
||||
JUNIT_TESTS_COUNT="0"
|
||||
JUNIT_SUCCESS_COUNT="0"
|
||||
JUNIT_FAILURES_COUNT="0"
|
||||
JUNIT_SKIPPED_COUNT="0"
|
||||
JUNIT_ERRORS_COUNT="0"
|
||||
|
||||
is_junit_output_enabled() {
|
||||
if [[ " ${MODES[@]} " =~ " junit-xml " ]]; then
|
||||
true
|
||||
else
|
||||
false
|
||||
fi
|
||||
}
|
||||
|
||||
xml_escape() {
|
||||
sed 's/&/\&/g; s/</\</g; s/>/\>/g; s/\"/\"/g; s/'"'"'/\'/g' <<< "$1"
|
||||
}
|
||||
|
||||
prepare_junit_output() {
|
||||
# Remove any JUnit output from previous runs
|
||||
rm -rf "$JUNIT_OUTPUT_DIRECTORY"
|
||||
mkdir "$JUNIT_OUTPUT_DIRECTORY"
|
||||
echo ""
|
||||
echo "$NOTICE Writing JUnit XML reports to $PROWLER_DIR/$JUNIT_OUTPUT_DIRECTORY $NORMAL"
|
||||
}
|
||||
|
||||
prepare_junit_check_output() {
|
||||
# JUnit test cases must be named uniquely, but each Prowler check can output many times due to multiple resources,
|
||||
# therefore append an index value to the test case name to provide uniqueness, reset it to 1 before starting this check
|
||||
JUNIT_CHECK_INDEX=1
|
||||
# To match JUnit behaviour in Java, and ensure that an aborted execution does not leave a partially written and therefore invalid XML file,
|
||||
# output a JUnit XML file per check
|
||||
JUNIT_OUTPUT_FILE="$JUNIT_OUTPUT_DIRECTORY/TEST-$1.xml"
|
||||
printf '%s\n' \
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" \
|
||||
"<testsuite name=\"$(xml_escape "$(get_junit_classname)")\" tests=\"_TESTS_COUNT_\" failures=\"_FAILURES_COUNT_\" skipped=\"_SKIPPED_COUNT_\" errors=\"_ERRORS_COUNT_\" timestamp=\"$(get_iso8601_timestamp)\">" \
|
||||
" <properties>" \
|
||||
" <property name=\"prowler.version\" value=\"$(xml_escape "$PROWLER_VERSION")\"/>" \
|
||||
" <property name=\"aws.profile\" value=\"$(xml_escape "$PROFILE")\"/>" \
|
||||
" <property name=\"aws.accountNumber\" value=\"$(xml_escape "$ACCOUNT_NUM")\"/>" \
|
||||
" <property name=\"check.id\" value=\"$(xml_escape "$TITLE_ID")\"/>" \
|
||||
" <property name=\"check.scored\" value=\"$(xml_escape "$ITEM_SCORED")\"/>" \
|
||||
" <property name=\"check.level\" value=\"$(xml_escape "$ITEM_LEVEL")\"/>" \
|
||||
" <property name=\"check.asff.type\" value=\"$(xml_escape "$ASFF_TYPE")\"/>" \
|
||||
" <property name=\"check.asff.resourceType\" value=\"$(xml_escape "$ASFF_RESOURCE_TYPE")\"/>" \
|
||||
" </properties>" \
|
||||
> "$JUNIT_OUTPUT_FILE"
|
||||
JUNIT_CHECK_START_TIME=$(get_time_in_milliseconds)
|
||||
}
|
||||
|
||||
finalise_junit_check_output() {
|
||||
# Calculate Total and populate summary info
|
||||
JUNIT_TESTS_COUNT=$((JUNIT_SUCCESS_COUNT+$JUNIT_FAILURES_COUNT+$JUNIT_SKIPPED_COUNT+$JUNIT_ERRORS_COUNT))
|
||||
sed "s/_TESTS_COUNT_/${JUNIT_TESTS_COUNT}/g;s/_FAILURES_COUNT_/${JUNIT_FAILURES_COUNT}/g;s/_SKIPPED_COUNT_/${JUNIT_SKIPPED_COUNT}/g;s/_ERRORS_COUNT_/${JUNIT_ERRORS_COUNT}/g" "$JUNIT_OUTPUT_FILE" > "$JUNIT_OUTPUT_FILE.$$"
|
||||
mv "$JUNIT_OUTPUT_FILE.$$" "$JUNIT_OUTPUT_FILE"
|
||||
echo '</testsuite>' >> "$JUNIT_OUTPUT_FILE"
|
||||
# Reset global counters as test output closed
|
||||
JUNIT_TESTS_COUNT="0"
|
||||
JUNIT_SUCCESS_COUNT="0"
|
||||
JUNIT_FAILURES_COUNT="0"
|
||||
JUNIT_SKIPPED_COUNT="0"
|
||||
JUNIT_ERRORS_COUNT="0"
|
||||
}
|
||||
|
||||
output_junit_success() {
|
||||
((JUNIT_SUCCESS_COUNT++))
|
||||
output_junit_test_case "$1" "<system-out>$(xml_escape "$1")</system-out>"
|
||||
}
|
||||
|
||||
output_junit_info() {
|
||||
# Nothing to output for JUnit for this level of message, but reset the check timer for timing the next check
|
||||
JUNIT_CHECK_START_TIME=$(get_time_in_milliseconds)
|
||||
}
|
||||
|
||||
output_junit_failure() {
|
||||
((JUNIT_FAILURES_COUNT++))
|
||||
output_junit_test_case "$1" "<failure message=\"$(xml_escape "$1")\"/>"
|
||||
}
|
||||
|
||||
output_junit_skipped() {
|
||||
((JUNIT_SKIPPED_COUNT++))
|
||||
output_junit_test_case "$1" "<skipped message=\"$(xml_escape "$1")\"/>"
|
||||
}
|
||||
|
||||
get_junit_classname() {
|
||||
# <section>.<check_id> naturally follows a Java package structure, so it is suitable as a package name
|
||||
echo "$TITLE_ID"
|
||||
}
|
||||
|
||||
output_junit_test_case() {
|
||||
local time_now
|
||||
local test_case_duration
|
||||
time_now=$(get_time_in_milliseconds)
|
||||
# JUnit test case time values are in seconds, so divide by 1000 using e-3 to convert from milliseconds without losing accuracy due to non-floating point arithmetic
|
||||
test_case_duration=$(printf "%.3f" "$((time_now - JUNIT_CHECK_START_TIME))e-3")
|
||||
printf '%s\n' \
|
||||
" <testcase name=\"$(xml_escape "$TITLE_TEXT") ($JUNIT_CHECK_INDEX)\" classname=\"$(xml_escape "$(get_junit_classname)")\" time=\"$test_case_duration\">" \
|
||||
" $2" \
|
||||
" </testcase>" >> "$JUNIT_OUTPUT_FILE"
|
||||
# Reset the check timer for timing the next check
|
||||
JUNIT_CHECK_START_TIME=$(get_time_in_milliseconds)
|
||||
((JUNIT_CHECK_INDEX+=1))
|
||||
}
|
||||
298
lib/common/os_detector
Normal file
298
lib/common/os_detector
Normal file
@@ -0,0 +1,298 @@
|
||||
#!/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.
|
||||
|
||||
DATE_CMD="date"
|
||||
BASE64_CMD="base64"
|
||||
|
||||
gnu_how_older_from_today() {
|
||||
DATE_TO_COMPARE=$1
|
||||
TODAY_IN_DAYS=$("$DATE_CMD" -d "$("$DATE_CMD" +%Y-%m-%d)" +%s)
|
||||
DATE_FROM_IN_DAYS=$("$DATE_CMD" -d $DATE_TO_COMPARE +%s)
|
||||
DAYS_SINCE=$((($TODAY_IN_DAYS - $DATE_FROM_IN_DAYS )/60/60/24))
|
||||
echo $DAYS_SINCE
|
||||
}
|
||||
bsd_how_older_from_today() {
|
||||
DATE_TO_COMPARE=$1
|
||||
TODAY_IN_DAYS=$("$DATE_CMD" +%s)
|
||||
DATE_FROM_IN_DAYS=$("$DATE_CMD" -jf %Y-%m-%d $DATE_TO_COMPARE +%s)
|
||||
DAYS_SINCE=$((($TODAY_IN_DAYS - $DATE_FROM_IN_DAYS )/60/60/24))
|
||||
echo $DAYS_SINCE
|
||||
}
|
||||
|
||||
# function to convert from timestamp to date
|
||||
# output date format %Y-%m-%d
|
||||
gnu_timestamp_to_date() {
|
||||
# if date comes from cli v2 in format like 2020-04-29T10:13:09.191000-04:00, which is ISO8601
|
||||
|
||||
# remove fractions of a second
|
||||
TIMESTAMP_TO_CONVERT=$(cut -f1 -d"." <<< "${1}")
|
||||
if ! "${DATE_CMD}" -d "${TIMESTAMP_TO_CONVERT}" +%s > /dev/null 2>&1;
|
||||
then
|
||||
# If input is epoch use the @
|
||||
OUTPUT_DATE=$("${DATE_CMD}" -d @"${TIMESTAMP_TO_CONVERT}" +'%Y-%m-%d')
|
||||
else
|
||||
# If input is not epoch dont use @
|
||||
OUTPUT_DATE=$("${DATE_CMD}" -d "${TIMESTAMP_TO_CONVERT}" +'%Y-%m-%d')
|
||||
fi
|
||||
echo "${OUTPUT_DATE}"
|
||||
}
|
||||
bsd_timestamp_to_date() {
|
||||
# if date comes from cli v2 in format like 2020-04-29T10:13:09.191000-04:00, which is ISO8601
|
||||
|
||||
# remove fractions of a second
|
||||
TIMESTAMP_TO_CONVERT=$(cut -f1 -d"." <<< "${1}")
|
||||
OUTPUT_DATE=$("${DATE_CMD}" -jf %Y-%m-%d "${TIMESTAMP_TO_CONVERT}" +%F 2>/dev/null)
|
||||
echo "${OUTPUT_DATE}"
|
||||
}
|
||||
|
||||
gnu_decode_report() {
|
||||
"$BASE64_CMD" -d
|
||||
}
|
||||
bsd_decode_report() {
|
||||
"$BASE64_CMD" -D
|
||||
}
|
||||
|
||||
gnu_how_many_days_from_today() {
|
||||
DATE_TO_COMPARE=$1
|
||||
TODAY_IN_DAYS=$("$DATE_CMD" -d "$("$DATE_CMD" +%Y-%m-%d)" +%s)
|
||||
DATE_IN_DAYS=$("$DATE_CMD" -d $DATE_TO_COMPARE +%s)
|
||||
DAYS_TO=$((( $DATE_IN_DAYS - $TODAY_IN_DAYS )/60/60/24))
|
||||
echo $DAYS_TO
|
||||
}
|
||||
bsd_how_many_days_from_today() {
|
||||
DATE_TO_COMPARE=$1
|
||||
TODAY_IN_DAYS=$("$DATE_CMD" +%s)
|
||||
DATE_IN_DAYS=$("$DATE_CMD" -jf %Y-%m-%d $DATE_TO_COMPARE +%s)
|
||||
DAYS_TO=$((( $DATE_IN_DAYS - $TODAY_IN_DAYS )/60/60/24))
|
||||
echo $DAYS_TO
|
||||
}
|
||||
|
||||
gnu_get_date_previous_than_months() {
|
||||
MONTHS_TO_COMPARE=$1
|
||||
MONTHS_TO_COMPARE_IN_SECONDS=$(( 60 * 60 * 24 * 31 * $MONTHS_TO_COMPARE ))
|
||||
CURRENTSECS=$("$DATE_CMD" +%s)
|
||||
STARTDATEINSECS=$(( $CURRENTSECS - $MONTHS_TO_COMPARE_IN_SECONDS ))
|
||||
DATE_BEFORE_MONTHS_TO_COMPARE=$("$DATE_CMD" -d @$STARTDATEINSECS '+%Y-%m-%d')
|
||||
echo $DATE_BEFORE_MONTHS_TO_COMPARE
|
||||
}
|
||||
bsd_get_date_previous_than_months() {
|
||||
MONTHS_TO_COMPARE=$1
|
||||
DATE_BEFORE_MONTHS_TO_COMPARE=$("$DATE_CMD" -v -$(echo $MONTHS_TO_COMPARE)m '+%Y-%m-%d')
|
||||
echo $DATE_BEFORE_MONTHS_TO_COMPARE
|
||||
}
|
||||
|
||||
gnu_get_time_in_milliseconds() {
|
||||
"$DATE_CMD" +%s%3N
|
||||
}
|
||||
bsd_get_time_in_milliseconds() {
|
||||
# BSD date does not support outputting milliseconds, so pad with zeros
|
||||
"$DATE_CMD" +%s000
|
||||
}
|
||||
|
||||
gnu_get_iso8601_timestamp() {
|
||||
"$DATE_CMD" -u +"%Y-%m-%dT%H:%M:%SZ"
|
||||
}
|
||||
|
||||
bsd_get_iso8601_timestamp() {
|
||||
"$DATE_CMD" -u +"%Y-%m-%dT%H:%M:%SZ"
|
||||
}
|
||||
|
||||
gnu_convert_date_to_timestamp() {
|
||||
# if [ "$OSTYPE" == "linux-musl" ]; then
|
||||
# date -D "%Y-%m-%dT%H:%M:%SZ" -d "$1" +%s
|
||||
# else
|
||||
date -u -d "$1" +%s
|
||||
# fi
|
||||
}
|
||||
|
||||
bsd_convert_date_to_timestamp() {
|
||||
echo $(date -u -j -f %Y-%m-%dT%H:%M:%S "$1" +%s)
|
||||
#date -j -f "%Y-%m-%dT%H:%M:%S" "$1" "+%s"
|
||||
}
|
||||
|
||||
gnu_test_tcp_connectivity() {
|
||||
HOST=$1
|
||||
PORT=$2
|
||||
TIMEOUT=$3
|
||||
# This is initially for ES port 9300, not not HTTP but I add HTTP error
|
||||
# codes for better handling, so 200 is open and 000 is not responding
|
||||
timeout $TIMEOUT bash -c '(echo > /dev/tcp/'$HOST'/'$PORT') >/dev/null 2>&1 && echo "200" || echo "000"'
|
||||
}
|
||||
bsd_test_tcp_connectivity() {
|
||||
HOST=$1
|
||||
PORT=$2
|
||||
TIMEOUT=$3
|
||||
# This is initially for ES port 9300, not not HTTP but I add HTTP error
|
||||
# codes for better handling, so 200 is open and 000 is not responding
|
||||
nc -z -G $TIMEOUT $HOST $PORT >/dev/null 2>&1 && echo "200" || echo "000"
|
||||
}
|
||||
|
||||
gnu_replace_sed(){
|
||||
sed -i $1 $2
|
||||
}
|
||||
|
||||
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
|
||||
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() {
|
||||
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
|
||||
455
lib/common/outputs
Normal file
455
lib/common/outputs
Normal file
@@ -0,0 +1,455 @@
|
||||
#!/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.
|
||||
|
||||
# Output formatting functions
|
||||
|
||||
EXTENSION_CSV="csv"
|
||||
EXTENSION_JSON="json"
|
||||
EXTENSION_ASFF="asff.json"
|
||||
EXTENSION_TEXT="txt"
|
||||
EXTENSION_HTML="html"
|
||||
OUTPUT_DATE=$(date -u +"%Y%m%d%H%M%S")
|
||||
OUTPUT_DIR="${PROWLER_DIR}/output" # default output if none
|
||||
if [[ $OUTPUT_DIR_CUSTOM ]]; then
|
||||
# output mode has to be set to other than text
|
||||
if [[ ! " ${MODES[@]} " =~ " text " || ${check_id} == 7.1 || ${check_id} == 7.74 ]]; then
|
||||
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 - Mode (-M) has to be set as well. Use -h for help."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
if [ -z ${OUTPUT_FILE_NAME+x} ]; then
|
||||
OUTPUT_FILE_NAME="${OUTPUT_DIR}/prowler-output-${ACCOUNT_NUM}-${OUTPUT_DATE}"
|
||||
else
|
||||
OUTPUT_FILE_NAME="${OUTPUT_DIR}/$OUTPUT_FILE_NAME"
|
||||
fi
|
||||
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=$@
|
||||
|
||||
# Available parameters for outputs formats (implemented this in CSV from v2.4):
|
||||
|
||||
# $PROFILE profile used to run Prowler (--profile in AWS CLI)
|
||||
# $ACCOUNT_NUM AWS Account ID
|
||||
# $REPREGION AWS region scanned
|
||||
# $TITLE_ID Numeric identifier of each check (1.2, 2.3, etc), originally based on CIS checks.
|
||||
# $CHECK_RESULT values can be PASS, FAIL, INFO or WARNING if allowlisted
|
||||
# $ITEM_SCORED corresponds to CHECK_SCORED, values can be Scored/Not Scored. This is CIS only, will be deprecated in Prowler.
|
||||
# $ITEM_CIS_LEVEL corresponds to CHECK_TYPE_ currently only for CIS Level 1, CIS Level 2 and Extras (all checks not part of CIS)
|
||||
# $TITLE_TEXT corresponds to CHECK_TITLE_ shows title of each check
|
||||
# $CHECK_RESULT_EXTENDED shows response of each check per resource like sg-123438 is open!
|
||||
# $CHECK_ASFF_COMPLIANCE_TYPE specify type from taxonomy https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-findings-format-type-taxonomy.html
|
||||
# $CHECK_SEVERITY severity Low, Medium, High, Critical
|
||||
# $CHECK_SERVICENAME AWS service name short name
|
||||
# $CHECK_ASFF_RESOURCE_TYPE values from https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-findings-format.html#asff-resources
|
||||
# $CHECK_ASFF_TYPE generic type from taxonomy here https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-findings-format-type-taxonomy.html
|
||||
# $CHECK_RISK text about risk
|
||||
# $CHECK_REMEDIATION text about remediation
|
||||
# $CHECK_DOC link to related documentation
|
||||
# $CHECK_CAF_EPIC it can be Logging and Monitoring, IAM, Data Protection, Infrastructure Security. Incident Response is not included since CAF has not specific checks on it logs enablement are part of Logging and Monitoring.
|
||||
# $ACCOUNT_DETAILS_EMAIL
|
||||
# $ACCOUNT_DETAILS_NAME
|
||||
# $ACCOUNT_DETAILS_ARN
|
||||
# $ACCOUNT_DETAILS_ORG
|
||||
# $ACCOUNT_DETAILS_TAGS
|
||||
|
||||
# Ensure that output directory always exists when -M is used
|
||||
if [[ $MODE ]];then
|
||||
mkdir -p "${OUTPUT_DIR}"
|
||||
if [[ "${MODES[@]}" =~ "html" ]]; then
|
||||
addHtmlHeader > ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
|
||||
HTML_REPORT_INIT="1"
|
||||
fi
|
||||
if [[ "${MODES[@]}" =~ "csv" ]]; then
|
||||
printCsvHeader
|
||||
fi
|
||||
fi
|
||||
|
||||
# textInfo "HTML report will be saved: ${OUTPUT_FILE_NAME}.$EXTENSION_HTML"
|
||||
# textInfo "JSON ASFF report will be saved: ${OUTPUT_FILE_NAME}.$EXTENSION_ASFF"
|
||||
# textInfo "CSV report will be saved: ${OUTPUT_FILE_NAME}.$EXTENSION_CSV"
|
||||
# textInfo "JSON report will be saved: ${OUTPUT_FILE_NAME}.$EXTENSION_JSON"
|
||||
|
||||
if [[ $PROFILE == "" ]];then
|
||||
PROFILE="ENV"
|
||||
fi
|
||||
|
||||
textPass(){
|
||||
CHECK_RESULT="PASS"
|
||||
CHECK_RESULT_EXTENDED="$1"
|
||||
CHECK_RESOURCE_ID="$3"
|
||||
|
||||
if [[ "$QUIET" == 1 ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
PASS_COUNTER=$((PASS_COUNTER+1))
|
||||
if [[ $2 ]]; then
|
||||
REPREGION=$2
|
||||
else
|
||||
REPREGION=$REGION
|
||||
fi
|
||||
|
||||
if [[ "${MODES[@]}" =~ "csv" ]]; then
|
||||
# Writting csv file replacing commas
|
||||
echo "${PROFILE/,/--}${SEP}${ACCOUNT_NUM/,/--}${SEP}${REPREGION/,/--}${SEP}${TITLE_ID/,/--}${SEP}${CHECK_RESULT/,/--}${SEP}${ITEM_SCORED/,/--}${SEP}${ITEM_CIS_LEVEL/,/--}${SEP}${TITLE_TEXT/,/--}${SEP}${CHECK_RESULT_EXTENDED/,/--}${SEP}${CHECK_ASFF_COMPLIANCE_TYPE/,/--}${SEP}${CHECK_SEVERITY/,/--}${SEP}${CHECK_SERVICENAME/,/--}${SEP}${CHECK_ASFF_RESOURCE_TYPE/,/--}${SEP}${CHECK_ASFF_TYPE/,/--}${SEP}${CHECK_RISK/,/--}${SEP}${CHECK_REMEDIATION/,/--}${SEP}${CHECK_DOC/,/--}${SEP}${CHECK_CAF_EPIC/,/--}${SEP}${CHECK_RESOURCE_ID/,/--}${SEP}${PROWLER_START_TIME/,/--}${SEP}${ACCOUNT_DETAILS_EMAIL/,/--}${SEP}${ACCOUNT_DETAILS_NAME/,/--}${SEP}${ACCOUNT_DETAILS_ARN/,/--}${SEP}${ACCOUNT_DETAILS_ORG/,/--}${SEP}${ACCOUNT_DETAILS_TAGS/,/--}" >> ${OUTPUT_FILE_NAME}.$EXTENSION_CSV
|
||||
fi
|
||||
if [[ "${MODES[@]}" =~ "json" ]]; then
|
||||
generateJsonOutput "$1" "Pass" "$CHECK_RESOURCE_ID" >> ${OUTPUT_FILE_NAME}.$EXTENSION_JSON
|
||||
fi
|
||||
if [[ "${MODES[@]}" =~ "json-asff" ]]; then
|
||||
JSON_ASFF_OUTPUT=$(generateJsonAsffOutput "$1" "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}" "${REPREGION}"
|
||||
fi
|
||||
fi
|
||||
if is_junit_output_enabled; then
|
||||
output_junit_success "$1"
|
||||
fi
|
||||
if [[ "${MODES[@]}" =~ "mono" ]]; then
|
||||
echo " $OK PASS!$NORMAL $1" >> ${OUTPUT_FILE_NAME}.$EXTENSION_TEXT
|
||||
fi
|
||||
# if [[ "${MODES[@]}" =~ "text" || "${MODES[@]}" =~ "mono" ]]; then
|
||||
# runs showing console output no mater what output is selected
|
||||
echo " $OK PASS!$NORMAL $1"
|
||||
# fi
|
||||
if [[ "${MODES[@]}" =~ "html" ]]; then
|
||||
generateHtmlOutput "$1" "PASS"
|
||||
fi
|
||||
}
|
||||
|
||||
textInfo(){
|
||||
CHECK_RESULT="INFO"
|
||||
CHECK_RESULT_EXTENDED="$1"
|
||||
CHECK_RESOURCE_ID="$3"
|
||||
|
||||
if [[ "$QUIET" == 1 ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ $2 ]]; then
|
||||
REPREGION=$2
|
||||
else
|
||||
REPREGION=$REGION
|
||||
fi
|
||||
|
||||
if [[ "${MODES[@]}" =~ "csv" ]]; then
|
||||
# Writing csv file replacing commas
|
||||
echo "${PROFILE/,/--}${SEP}${ACCOUNT_NUM/,/--}${SEP}${REPREGION/,/--}${SEP}${TITLE_ID/,/--}${SEP}${CHECK_RESULT/,/--}${SEP}${ITEM_SCORED/,/--}${SEP}${ITEM_CIS_LEVEL/,/--}${SEP}${TITLE_TEXT/,/--}${SEP}${CHECK_RESULT_EXTENDED/,/--}${SEP}${CHECK_ASFF_COMPLIANCE_TYPE/,/--}${SEP}${CHECK_SEVERITY/,/--}${SEP}${CHECK_SERVICENAME/,/--}${SEP}${CHECK_ASFF_RESOURCE_TYPE/,/--}${SEP}${CHECK_ASFF_TYPE/,/--}${SEP}${CHECK_RISK/,/--}${SEP}${CHECK_REMEDIATION/,/--}${SEP}${CHECK_DOC/,/--}${SEP}${CHECK_CAF_EPIC/,/--}${SEP}${CHECK_RESOURCE_ID/,/--}${SEP}${PROWLER_START_TIME/,/--}${SEP}${ACCOUNT_DETAILS_EMAIL/,/--}${SEP}${ACCOUNT_DETAILS_NAME/,/--}${SEP}${ACCOUNT_DETAILS_ARN/,/--}${SEP}${ACCOUNT_DETAILS_ORG/,/--}${SEP}${ACCOUNT_DETAILS_TAGS/,/--}" >> ${OUTPUT_FILE_NAME}.$EXTENSION_CSV
|
||||
fi
|
||||
if [[ "${MODES[@]}" =~ "json" ]]; then
|
||||
generateJsonOutput "$1" "Info" "$CHECK_RESOURCE_ID" >> ${OUTPUT_FILE_NAME}.${EXTENSION_JSON}
|
||||
fi
|
||||
if is_junit_output_enabled; then
|
||||
output_junit_info "$1"
|
||||
fi
|
||||
if [[ "${MODES[@]}" =~ "mono" ]]; then
|
||||
echo " $NOTICE INFO! $1 $NORMAL" >> ${OUTPUT_FILE_NAME}.$EXTENSION_TEXT
|
||||
fi
|
||||
# if [[ "${MODES[@]}" =~ "text" ]]; then
|
||||
echo " $NOTICE INFO! $1 $NORMAL"
|
||||
# fi
|
||||
if [[ "${MODES[@]}" =~ "html" ]]; then
|
||||
generateHtmlOutput "$1" "INFO" "$CHECK_RESOURCE_ID"
|
||||
fi
|
||||
}
|
||||
|
||||
textFail(){
|
||||
## ignore allowlists for current check
|
||||
level="FAIL"
|
||||
colorcode="$BAD"
|
||||
while read -r excluded_item; do
|
||||
# ignore_check_name is the check with resources allowlisted
|
||||
ignore_check_name=$(awk -F ":" '{print $1}' <<< "${excluded_item}")
|
||||
# Resource value is what it comes after CHECK_NAME: :
|
||||
resource_value=$(awk -F "$CHECK_NAME:" '{print $2}' <<< "${excluded_item}")
|
||||
# Checked value is the whole log message that comes as argument
|
||||
checked_value=$1
|
||||
if [[ "${ignore_check_name}" != "${CHECK_NAME}" ]]; then
|
||||
# not for this check
|
||||
continue
|
||||
fi
|
||||
# To set WARNING flag checked_value have to include value of resource_value
|
||||
# If it is treated as only expanse (*[]*) will not detect regex like [:alpha:]
|
||||
if [[ "${checked_value}" =~ ${resource_value} ]]; then
|
||||
level="WARNING"
|
||||
colorcode="${WARNING}"
|
||||
break
|
||||
fi
|
||||
done <<< "$IGNORES"
|
||||
|
||||
# only set non-0 exit code on FAIL mode, WARN is ok
|
||||
if [[ "$level" == "FAIL" ]]; then
|
||||
FAIL_COUNTER=$((FAIL_COUNTER+1))
|
||||
if [ "$FAILED_CHECK_FAILED_SCAN" == 1 ] && [ -z "$FAILED_CHECK_FAILED_SCAN_LIST" ] ; then
|
||||
EXITCODE=3
|
||||
fi
|
||||
if [[ "${FAILED_CHECK_FAILED_SCAN_LIST[@]}" =~ "$CHECK_NAME" ]]; then
|
||||
EXITCODE=3
|
||||
fi
|
||||
fi
|
||||
|
||||
CHECK_RESULT=$level
|
||||
CHECK_RESULT_EXTENDED="$1"
|
||||
CHECK_RESOURCE_ID="$3"
|
||||
|
||||
if [[ $2 ]]; then
|
||||
REPREGION=$2
|
||||
else
|
||||
REPREGION=$REGION
|
||||
fi
|
||||
|
||||
if [[ "${MODES[@]}" =~ "csv" ]]; then
|
||||
# Writing csv file replacing commas
|
||||
echo "${PROFILE/,/--}${SEP}${ACCOUNT_NUM/,/--}${SEP}${REPREGION/,/--}${SEP}${TITLE_ID/,/--}${SEP}${CHECK_RESULT/,/--}${SEP}${ITEM_SCORED/,/--}${SEP}${ITEM_CIS_LEVEL/,/--}${SEP}${TITLE_TEXT/,/--}${SEP}${CHECK_RESULT_EXTENDED/,/--}${SEP}${CHECK_ASFF_COMPLIANCE_TYPE/,/--}${SEP}${CHECK_SEVERITY/,/--}${SEP}${CHECK_SERVICENAME/,/--}${SEP}${CHECK_ASFF_RESOURCE_TYPE/,/--}${SEP}${CHECK_ASFF_TYPE/,/--}${SEP}${CHECK_RISK/,/--}${SEP}${CHECK_REMEDIATION/,/--}${SEP}${CHECK_DOC/,/--}${SEP}${CHECK_CAF_EPIC/,/--}${SEP}${CHECK_RESOURCE_ID/,/--}${SEP}${PROWLER_START_TIME/,/--}${SEP}${ACCOUNT_DETAILS_EMAIL/,/--}${SEP}${ACCOUNT_DETAILS_NAME/,/--}${SEP}${ACCOUNT_DETAILS_ARN/,/--}${SEP}${ACCOUNT_DETAILS_ORG/,/--}${SEP}${ACCOUNT_DETAILS_TAGS/,/--}" >> ${OUTPUT_FILE_NAME}.$EXTENSION_CSV
|
||||
fi
|
||||
if [[ "${MODES[@]}" =~ "json" ]]; then
|
||||
generateJsonOutput "$1" "${level}" "$CHECK_RESOURCE_ID">> ${OUTPUT_FILE_NAME}.${EXTENSION_JSON}
|
||||
fi
|
||||
if [[ "${MODES[@]}" =~ "json-asff" ]]; then
|
||||
JSON_ASFF_OUTPUT=$(generateJsonAsffOutput "$1" "${level}" "$CHECK_RESOURCE_ID")
|
||||
echo "${JSON_ASFF_OUTPUT}" >> ${OUTPUT_FILE_NAME}.${EXTENSION_ASFF}
|
||||
if [[ "${SEND_TO_SECURITY_HUB}" -eq 1 ]]; then
|
||||
sendToSecurityHub "${JSON_ASFF_OUTPUT}" "${REPREGION}"
|
||||
fi
|
||||
fi
|
||||
if is_junit_output_enabled; then
|
||||
if [[ "${level}" == "FAIL" ]]; then
|
||||
output_junit_failure "$1"
|
||||
elif [[ "${level}" == "WARNING" ]]; then
|
||||
output_junit_skipped "$1"
|
||||
fi
|
||||
fi
|
||||
if [[ "${MODES[@]}" =~ "mono" ]]; then
|
||||
echo " $colorcode ${level}! $1 $NORMAL" >> ${OUTPUT_FILE_NAME}.$EXTENSION_TEXT
|
||||
fi
|
||||
# if [[ "${MODES[@]}" =~ "text" ]]; then
|
||||
echo " $colorcode ${level}! $1 $NORMAL"
|
||||
# fi
|
||||
if [[ "${MODES[@]}" =~ "html" ]]; then
|
||||
generateHtmlOutput "$1" "${level}" "$CHECK_RESOURCE_ID"
|
||||
fi
|
||||
}
|
||||
|
||||
textTitle(){
|
||||
CHECKS_COUNTER=$((CHECKS_COUNTER+1))
|
||||
TITLE_ID="$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")
|
||||
fi
|
||||
|
||||
TITLE_TEXT=$2
|
||||
local CHECK_SERVICENAME="$MAGENTA$3$NORMAL"
|
||||
local CHECK_SEVERITY="$BROWN[$4]$NORMAL"
|
||||
|
||||
case "$6" in
|
||||
LEVEL1) ITEM_CIS_LEVEL="CIS Level 1";;
|
||||
LEVEL2) ITEM_CIS_LEVEL="CIS Level 2";;
|
||||
EXTRA) ITEM_CIS_LEVEL="Extra";;
|
||||
SUPPORT) ITEM_CIS_LEVEL="Support";;
|
||||
*) ITEM_CIS_LEVEL="Unspecified or Invalid";;
|
||||
esac
|
||||
|
||||
local group_ids
|
||||
# if [[ -n "$4" ]]; then
|
||||
group_ids="$CYAN[$5]$NORMAL"
|
||||
# fi
|
||||
|
||||
# if [[ "${MODES[@]}" =~ "csv" ]]; then
|
||||
# >&2 echo "$TITLE_ID $TITLE_TEXT" >> ${OUTPUT_FILE_NAME}.${EXTENSION_CSV}
|
||||
# elif [[ "${MODES[@]}" =~ "json" || "${MODES[@]}" =~ "json-asff" ]]; then
|
||||
# :
|
||||
# else
|
||||
# # if [[ "$ITEM_SCORED" == "Scored" ]]; then
|
||||
# echo -e "$TITLE_ID $CHECK_SERVICENAME $TITLE_TEXT $CHECK_SEVERITY $group_ids "
|
||||
echo -e "$TITLE_ID $TITLE_TEXT - $CHECK_SERVICENAME $CHECK_SEVERITY"
|
||||
# # else
|
||||
# # echo -e "\n$PURPLE $TITLE_ID $TITLE_TEXT $6 $NORMAL $group_ids "
|
||||
# # fi
|
||||
# fi
|
||||
}
|
||||
|
||||
generateJsonOutput(){
|
||||
local message=$1
|
||||
local status=$2
|
||||
local resource_id=$3
|
||||
jq -M -c \
|
||||
--arg PROFILE "$PROFILE" \
|
||||
--arg ACCOUNT_NUM "$ACCOUNT_NUM" \
|
||||
--arg TITLE_TEXT "$TITLE_TEXT" \
|
||||
--arg MESSAGE "$(echo -e "${message}" | sed -e 's/^[[:space:]]*//')" \
|
||||
--arg STATUS "$status" \
|
||||
--arg SEVERITY "$(echo $CHECK_SEVERITY | sed 's/[][]//g')" \
|
||||
--arg SCORED "$ITEM_SCORED" \
|
||||
--arg ITEM_CIS_LEVEL "$ITEM_CIS_LEVEL" \
|
||||
--arg TITLE_ID "$TITLE_ID" \
|
||||
--arg REPREGION "$REPREGION" \
|
||||
--arg TYPE "$CHECK_ASFF_COMPLIANCE_TYPE" \
|
||||
--arg TIMESTAMP "$(get_iso8601_timestamp)" \
|
||||
--arg SERVICENAME "$CHECK_SERVICENAME" \
|
||||
--arg CHECK_CAF_EPIC "$CHECK_CAF_EPIC" \
|
||||
--arg CHECK_RISK "$CHECK_RISK" \
|
||||
--arg CHECK_REMEDIATION "$CHECK_REMEDIATION" \
|
||||
--arg CHECK_DOC "$CHECK_DOC" \
|
||||
--arg CHECK_RESOURCE_ID "$resource_id" \
|
||||
--arg ACCOUNT_DETAILS_EMAIL "$ACCOUNT_DETAILS_EMAIL" \
|
||||
--arg ACCOUNT_DETAILS_NAME "$ACCOUNT_DETAILS_NAME" \
|
||||
--arg ACCOUNT_DETAILS_ARN "$ACCOUNT_DETAILS_ARN" \
|
||||
--arg ACCOUNT_DETAILS_ORG "$ACCOUNT_DETAILS_ORG" \
|
||||
--arg ACCOUNT_DETAILS_TAGS "$ACCOUNT_DETAILS_TAGS" \
|
||||
-n '{
|
||||
"Profile": $PROFILE,
|
||||
"Account Number": $ACCOUNT_NUM,
|
||||
"Control": $TITLE_TEXT,
|
||||
"Message": $MESSAGE,
|
||||
"Severity": $SEVERITY,
|
||||
"Status": $STATUS,
|
||||
"Scored": $SCORED,
|
||||
"Level": $ITEM_CIS_LEVEL,
|
||||
"Control ID": $TITLE_ID,
|
||||
"Region": $REPREGION,
|
||||
"Timestamp": $TIMESTAMP,
|
||||
"Compliance": $TYPE,
|
||||
"Service": $SERVICENAME,
|
||||
"CAF Epic": $CHECK_CAF_EPIC,
|
||||
"Risk": $CHECK_RISK,
|
||||
"Remediation": $CHECK_REMEDIATION,
|
||||
"Doc link": $CHECK_DOC,
|
||||
"Resource ID": $CHECK_RESOURCE_ID,
|
||||
"Account Email": $ACCOUNT_DETAILS_EMAIL,
|
||||
"Account Name": $ACCOUNT_DETAILS_NAME,
|
||||
"Account ARN": $ACCOUNT_DETAILS_ARN,
|
||||
"Account Organization": $ACCOUNT_DETAILS_ORG,
|
||||
"Account tags": $ACCOUNT_DETAILS_TAGS
|
||||
}'
|
||||
}
|
||||
|
||||
generateJsonAsffOutput(){
|
||||
# UNIQUE_ID must only contain characters from the unreserved characters set defined in section 2.3 of RFC-3986
|
||||
# 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
|
||||
jq -M -c \
|
||||
--arg ACCOUNT_NUM "$ACCOUNT_NUM" \
|
||||
--arg TITLE_TEXT "$CHECK_SERVICENAME.$TITLE_TEXT" \
|
||||
--arg MESSAGE "$(echo -e "${message}")" \
|
||||
--arg UNIQUE_ID "$(LC_ALL=C echo -e -n "${message}" | tr -cs '[:alnum:]._~-' '_')" \
|
||||
--arg STATUS "$status" \
|
||||
--arg SEVERITY "$(echo $CHECK_SEVERITY| awk '{ print toupper($0) }' | sed 's/[][]//g')" \
|
||||
--arg TITLE_ID "$TITLE_ID" \
|
||||
--arg CHECK_ID "$CHECK_ID" \
|
||||
--arg TYPE "$CHECK_ASFF_COMPLIANCE_TYPE" \
|
||||
--arg COMPLIANCE_RELATED_REQUIREMENTS "$CHECK_ASFF_COMPLIANCE_TYPE" \
|
||||
--arg RESOURCE_TYPE "$CHECK_ASFF_RESOURCE_TYPE" \
|
||||
--arg REPREGION "$REPREGION" \
|
||||
--arg TIMESTAMP "$(get_iso8601_timestamp)" \
|
||||
--arg PROWLER_VERSION "$PROWLER_VERSION" \
|
||||
--arg AWS_PARTITION "$AWS_PARTITION" \
|
||||
--arg CHECK_RESOURCE_ID "$resource_id" \
|
||||
-n '{
|
||||
"SchemaVersion": "2018-10-08",
|
||||
"Id": "prowler-\($TITLE_ID)-\($ACCOUNT_NUM)-\($REPREGION)-\($UNIQUE_ID)",
|
||||
"ProductArn": "arn:\($AWS_PARTITION):securityhub:\($REPREGION)::product/prowler/prowler",
|
||||
"RecordState": "ACTIVE",
|
||||
"ProductFields": {
|
||||
"ProviderName": "Prowler",
|
||||
"ProviderVersion": $PROWLER_VERSION,
|
||||
"ProwlerResourceName": $CHECK_RESOURCE_ID
|
||||
},
|
||||
"GeneratorId": "prowler-\($CHECK_ID)",
|
||||
"AwsAccountId": $ACCOUNT_NUM,
|
||||
"Types": [
|
||||
$TYPE
|
||||
],
|
||||
"FirstObservedAt": $TIMESTAMP,
|
||||
"UpdatedAt": $TIMESTAMP,
|
||||
"CreatedAt": $TIMESTAMP,
|
||||
"Severity": {
|
||||
"Label": $SEVERITY
|
||||
},
|
||||
"Title": $TITLE_TEXT,
|
||||
"Description": $MESSAGE,
|
||||
"Resources": [
|
||||
{
|
||||
"Type": $RESOURCE_TYPE,
|
||||
"Id": $CHECK_RESOURCE_ID,
|
||||
"Partition": $AWS_PARTITION,
|
||||
"Region": $REPREGION
|
||||
}
|
||||
],
|
||||
"Compliance": {
|
||||
"Status": $STATUS,
|
||||
"RelatedRequirements": [ $COMPLIANCE_RELATED_REQUIREMENTS ]
|
||||
}
|
||||
|
||||
}'
|
||||
}
|
||||
|
||||
generateHtmlOutput(){
|
||||
local message=$1
|
||||
local status=$2
|
||||
|
||||
if [[ $status == "INFO" ]];then
|
||||
local ROW_CLASS='table-info'
|
||||
fi
|
||||
if [[ $status == "PASS" ]];then
|
||||
local ROW_CLASS='p-3 mb-2 bg-success-custom'
|
||||
fi
|
||||
if [[ $status == "FAIL" ]];then
|
||||
local ROW_CLASS='table-danger'
|
||||
fi
|
||||
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
|
||||
echo ' <td>'$status'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
|
||||
echo ' <td>'$CHECK_SEVERITY'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
|
||||
echo ' <td>'$ACCOUNT_NUM'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
|
||||
echo ' <td>'$REPREGION'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
|
||||
echo ' <td>'$CHECK_ASFF_COMPLIANCE_TYPE'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
|
||||
echo ' <td>'$CHECK_SERVICENAME'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
|
||||
echo ' <td>'$TITLE_ID'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
|
||||
echo ' <td>'$TITLE_TEXT'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
|
||||
echo ' <td>'$message'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
|
||||
echo ' <td>'$ITEM_CIS_LEVEL'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
|
||||
echo ' <td>'$CHECK_CAF_EPIC'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
|
||||
echo ' <td><p class="show-read-more">'$CHECK_RISK'</p></td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
|
||||
echo ' <td><p class="show-read-more">'$CHECK_REMEDIATION'</p></td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
|
||||
echo ' <td><a class="read-more" href="'$CHECK_DOC'"><i class="fas fa-external-link-alt"></i></a></td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
|
||||
echo ' <td>'$CHECK_RESOURCE_ID'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
|
||||
echo '</tr>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
|
||||
echo '' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
|
||||
}
|
||||
32
lib/common/python_detector
Normal file
32
lib/common/python_detector
Normal file
@@ -0,0 +1,32 @@
|
||||
#!/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
|
||||
}
|
||||
65
lib/common/scoring
Normal file
65
lib/common/scoring
Normal file
@@ -0,0 +1,65 @@
|
||||
#!/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.
|
||||
|
||||
# Scoring POC
|
||||
scoring(){
|
||||
if [[ ! $PASS_COUNTER ]]; then
|
||||
PASS_COUNTER=0
|
||||
fi
|
||||
if [[ ! $FAIL_COUNTER ]]; then
|
||||
FAIL_COUNTER=0
|
||||
fi
|
||||
|
||||
# TOTAL_RESOURCES=$(awk "BEGIN {print $FAIL_COUNTER+$PASS_COUNTER; exit}")
|
||||
TOTAL_RESOURCES=$(($FAIL_COUNTER + $PASS_COUNTER))
|
||||
# Score is % of passed compared to failures. The higher the better
|
||||
if [[ $PASS_COUNTER == "0" ]]; then
|
||||
PROWLER_SCORE="0"
|
||||
else
|
||||
PROWLER_SCORE=$(( $PASS_COUNTER * 100 / $TOTAL_RESOURCES ))
|
||||
|
||||
fi
|
||||
|
||||
if [[ $SCORING == "1" ]]; then
|
||||
echo -e "$BLUE------------------------------------------------------------------ $NORMAL"
|
||||
echo -e "$CYAN _"
|
||||
echo -e " _ __ _ __ _____ _| | ___ _ __"
|
||||
echo -e " | '_ \| '__/ _ \ \ /\ / / |/ _ \ '__|"
|
||||
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 "\n$BLUE------------------------------------------------------------------ $NORMAL"
|
||||
echo -e " Security Assessment Summary Report for AWS Account: $ACCOUNT_NUM $NORMAL"
|
||||
echo -e "$BLUE------------------------------------------------------------------ $NORMAL"
|
||||
echo -e " Your Prowler Score* is = $PROWLER_SCORE $NORMAL "
|
||||
echo -e "$BLUE------------------------------------------------------------------ $NORMAL"
|
||||
echo -e "$BAD FAIL$NORMAL =$BAD $FAIL_COUNTER $NORMAL"
|
||||
echo -e "$BLUE------------------------------------------------------------------ $NORMAL"
|
||||
echo -e "$OK PASS$NORMAL =$OK $PASS_COUNTER $NORMAL"
|
||||
echo -e "$BLUE------------------------------------------------------------------ $NORMAL"
|
||||
echo -e " Total Resources Reviewed =$NOTICE $TOTAL_RESOURCES $NORMAL"
|
||||
echo -e "$BLUE------------------------------------------------------------------ $NORMAL"
|
||||
echo -e " Checks Performed =$NOTICE $CHECKS_COUNTER $NORMAL"
|
||||
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
|
||||
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
|
||||
replace_sed 's/FAIL_COUNTER/'$FAIL_COUNTER'/g' ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
|
||||
replace_sed 's/CHECKS_COUNTER/'$CHECKS_COUNTER'/g' ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
|
||||
fi
|
||||
}
|
||||
54
lib/common/secrets_detector
Normal file
54
lib/common/secrets_detector
Normal file
@@ -0,0 +1,54 @@
|
||||
#!/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 and configuration of detect-secrets
|
||||
secretsDetector(){
|
||||
PYTHON_PIP_DETECTSECRETS=$(which detect-secrets)
|
||||
if [ -z "${PYTHON_PIP_DETECTSECRETS}" ]; 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
|
||||
fi
|
||||
PYTHON_PIP_DETECTSECRETS_INSTALLED=1
|
||||
# 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.
|
||||
# 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)"'
|
||||
# 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
|
||||
;;
|
||||
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
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
95
lib/common/whoami
Normal file
95
lib/common/whoami
Normal file
@@ -0,0 +1,95 @@
|
||||
#!/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.
|
||||
|
||||
|
||||
# Get whoami in AWS, who is the user running this shell script
|
||||
# Get a list of all available AWS Regions
|
||||
# sice describe-regions doesn't seem to work at me-south-1|eu-south-1|ap-east-1|af-south-1.
|
||||
# Probably dased on https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html
|
||||
# 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) ):
|
||||
|
||||
|
||||
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
|
||||
echo -e "$RED ERROR Getting credentials to run Prowler - EXITING! $NORMAL"
|
||||
EXITCODE=2
|
||||
exit $EXITCODE
|
||||
fi
|
||||
fi
|
||||
|
||||
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
|
||||
fi
|
||||
}
|
||||
Reference in New Issue
Block a user