refactor(outputs): Consolidate Prowler output functions (#1180)

* chore(db providers): db providers first version

* chore(db provider): added db provider setup into Readme

* fix(csv_line): csv_line out of conditional

* fix(README): text instead of varchar in table

* fix(help): help message extended

Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>

* fix(typo): Update README.md

Co-authored-by: Pepe Fagoaga <pepe@verica.io>

* fix(table): add if not exists

Co-authored-by: Pepe Fagoaga <pepe@verica.io>

* fix(typo): Readme postgreSQL

Co-authored-by: Pepe Fagoaga <pepe@verica.io>

* fix(db_connector): details to add a new provider

* fix(typo): Uppercase Prowler

Co-authored-by: Toni de la Fuente <toni@blyx.com>

* fix(prowler): deleted unused variable

* chore(checks): test db connector previous to send data

* chore(input tests): input tests moved to main

* fix(typo): Readme typos

* chore(table): table name from pgpass file

* fix(grep test): Added missing -E flag

* chore(table): check of table name and Readme

* chore(error colors): Added error colors

* chore(inputcheck): checks about mode and output inputs into main

* fix(inputs) custom output file name

* fix(outputs): comment profile

* chore(textXXX): both 3 textfunctions using general

* fix(allowlist): allowlist check included as function

* fix(headers): Add headers to certain output files

* fix(reformulate): change structure and delete comments

* fix(testing): Input test after load includes

* fix(variables): Added named vars

* fix(colors): Deleted unused colors

* fix(outputs): fine tuning

* fix(outputs): allowlist parameters read

* fix(allowlist): allowlist logic reformulated

* fix(REPREGION): REPREGION change by REGION_FROM_CHECK

Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
Co-authored-by: Pepe Fagoaga <pepe@verica.io>
Co-authored-by: Toni de la Fuente <toni@blyx.com>
This commit is contained in:
Nacho Rivera
2022-06-06 12:56:21 +02:00
committed by GitHub
parent 1b63256b9c
commit bc56c4242e
7 changed files with 260 additions and 298 deletions
+2
View File
@@ -340,6 +340,7 @@ Jump into the section for the database provider you want to use and follow the r
- Mac -> `brew install libpq`
- Ubuntu -> `sudo apt-get install postgresql-client `
- RHEL/Centos -> `sudo yum install postgresql10`
- Configure a `~/.pgpass` file into the root folder of the user that is going to launch Prowler ([pgpass file doc](https://www.postgresql.org/docs/current/libpq-pgpass.html)), including an extra field at the end of the line, separated by `:`, to name the table, for instance:
`hostname:port:database:username:password:prowler_findings`
- Create a table in your PostgreSQL database to store the Prowler's data. You can use the following SQL statement to create the table:
@@ -372,6 +373,7 @@ account_details_org TEXT,
account_details_tags TEXT
);
```
- Execute Prowler with `-d` flag, for example:
`./prowler -M csv -d postgres`
> *Note*: This command creates a `csv` output file and stores the Prowler output in the configured PostgreSQL DB. It's an example, `-d` flag **does not** require `-M` to run.
+13 -52
View File
@@ -12,58 +12,19 @@
# 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=""
WARNING="" # Warning (brown)
SECTION="" # Section (yellow)
NOTICE="" # Notice (yellow)
OK="" # Ok (green)
BAD="" # Bad (red)
CYAN=""
BLUE=""
BROWN=""
DARKGRAY=""
GRAY=""
GREEN=""
MAGENTA=""
PURPLE=""
RED=""
YELLOW=""
WHITE=""
fi
# 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=""
printColorsCode(){
if [[ $MONOCHROME -eq 0 ]]; then
-1
View File
@@ -28,7 +28,6 @@ postgresql_connector () {
CSV_REGISTRY="${1}"
psql -q -U "${PSQL_USER}" -h "${PSQL_HOSTNAME}" -c "copy ${PSQL_TABLE} from stdin with null as E'\'\'' delimiter ','" <<< "${CSV_REGISTRY}"
}
db_exit_abnormally() {
+1 -1
View File
@@ -18,7 +18,7 @@ addHtmlHeader() {
PROFILE="ENV"
fi
if [[ -z $HTML_REPORT_INIT ]]; then
cat <<EOF
cat <<EOF >> "${OUTPUT_FILE_NAME}"."${EXTENSION_HTML}"
<!DOCTYPE html>
<html lang="en">
<head>
+182 -237
View File
@@ -11,45 +11,12 @@
# 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):
# Output format
# $PROFILE profile used to run Prowler (--profile in AWS CLI)
# $ACCOUNT_NUM AWS Account ID
# $REPREGION AWS region scanned
# $REGION_FROM_CHECK 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.
@@ -71,249 +38,227 @@ PROWLER_PARAMETERS=$@
# $ACCOUNT_DETAILS_ORG
# $ACCOUNT_DETAILS_TAGS
EXTENSION_CSV="csv"
EXTENSION_JSON="json"
EXTENSION_ASFF="asff.json"
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=$@
# 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"
# Set exitcode for codebuild/jenkins-like integrations
set_exitcode () {
if [ "${FAILED_CHECK_FAILED_SCAN}" == 1 ] && [ -z "${FAILED_CHECK_FAILED_SCAN_LIST}" ]
then
EXITCODE=3
export EXITCODE
fi
if [[ "${MODES[@]}" =~ "csv" ]]; then
printCsvHeader
if [[ "${FAILED_CHECK_FAILED_SCAN_LIST}" =~ ${CHECK_NAME} ]]
then
EXITCODE=3
export EXITCODE
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
CSV_LINE="${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/,/--}"
if [[ "${MODES[@]}" =~ "csv" ]]; then
# Writting csv file replacing commas
echo "${CSV_LINE}" >> ${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}"
# Add header to certain output files readed from mode
output_files_init() {
for MODE in "${MODES[@]}";
do
if [ "${MODE}" == 'html' ]
then
addHtmlHeader
HTML_REPORT_INIT="1"
export HTML_REPORT_INIT
fi
if [ "${MODE}" == 'csv' ]
then
printCsvHeader
fi
done
}
# Check if resource checked is allowlisted
allowlist_check() {
# Checked value is the whole log message that comes as argument
CHECKED_VALUE=${1}
# Default value of result is Fail
CHECK_RESULT='FAIL'
## ignore allowlists for current check
while read -r excluded_item; do
# 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}")
if [[ "${IGNORE_CHECK_NAME}" == "${CHECK_NAME}" ]]
then
if [[ "${CHECKED_VALUE}" =~ ${RESOURCE_VALUE} ]]
then
CHECK_RESULT="WARNING"
break
fi
fi
done <<< "$IGNORES"
echo "${CHECK_RESULT}"
}
is_quiet() {
IS_QUIET="${1}"
if [ "${IS_QUIET}" -eq 1 ]
then
return 0
else
return 1
fi
if is_junit_output_enabled; then
output_junit_success "$1"
}
general_output() {
CHECK_RESULT="${1}"
CHECK_RESULT_EXTENDED="${2}"
CHECK_RESOURCE_ID="${3}"
REGION_FROM_CHECK="${4}"
CSV_LINE="${PROFILE/,/--}${SEP}${ACCOUNT_NUM/,/--}${SEP}${REGION_FROM_CHECK/,/--}${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/,/--}"
COLOR_CODE="$OK"
# Check if color is different than normal (pass)
if [ "${CHECK_RESULT}" == 'INFO' ]
then
COLOR_CODE="${NOTICE}"
elif [ "${CHECK_RESULT}" == 'FAIL' ]
then
COLOR_CODE="${BAD}"
elif [ "${CHECK_RESULT}" == 'WARNING' ]
then
COLOR_CODE="${WARNING}"
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"
# Check if region is passed
if [ -z "${REGION_FROM_CHECK}" ]
then
REGION_FROM_CHECK=$REGION
fi
# Iterating over input modes
for MODE in "${MODES[@]}";
do
if [ "${MODE}" == 'html' ]
then
generateHtmlOutput "${CHECK_RESULT_EXTENDED}" "${CHECK_RESULT}"
elif [ "${MODE}" == 'csv' ]
then
echo "${CSV_LINE}" >> "${OUTPUT_FILE_NAME}"."${EXTENSION_CSV}"
elif [ "${MODE}" == 'json' ]
then
generateJsonOutput "${CHECK_RESULT_EXTENDED}" "${CHECK_RESULT}" "$CHECK_RESOURCE_ID" >> "${OUTPUT_FILE_NAME}"."${EXTENSION_JSON}"
elif [ "${MODE}" == '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}"
fi
elif is_junit_output_enabled
then
if [ "${CHECK_RESULT}" == 'PASS' ]
then
output_junit_success "${CHECK_RESULT_EXTENDED}"
elif [ "${CHECK_RESULT}" == 'INFO' ]
then
output_junit_info "${CHECK_RESULT_EXTENDED}"
elif [ "${CHECK_RESULT}" == 'FAIL' ]
then
output_junit_failure "${CHECK_RESULT_EXTENDED}"
elif [ "${CHECK_RESULT}" == 'WARNING' ]
then
output_junit_skipped "${CHECK_RESULT_EXTENDED}"
fi
elif [ "${MODE}" == 'mono' ]
then
echo " $COLOR_CODE ${CHECK_RESULT}! $NORMAL ${CHECK_RESULT_EXTENDED}">> "${OUTPUT_FILE_NAME}"."${EXTENSION_TEXT}"
fi
done
# Print in screen
echo " $COLOR_CODE ${CHECK_RESULT}!$NORMAL ${CHECK_RESULT_EXTENDED}"
#checking database provider
if [[ ${DATABASE_PROVIDER} == 'postgresql' ]]
then
postgresql_connector "${CSV_LINE}"
fi
}
textPass(){
CHECK_RESULT="PASS"
CHECK_RESULT_EXTENDED="${1}"
CHECK_RESOURCE_ID="${3}"
CHECK_REGION="${2}"
if is_quiet "${QUIET}"
then
return
fi
general_output "${CHECK_RESULT}" "${CHECK_RESULT_EXTENDED}" "${CHECK_RESOURCE_ID}" "${CHECK_REGION}"
}
textInfo(){
CHECK_RESULT="INFO"
CHECK_RESULT_EXTENDED="$1"
CHECK_RESOURCE_ID="$3"
CHECK_RESULT_EXTENDED="${1}"
CHECK_RESOURCE_ID="${3}"
CHECK_REGION="${2}"
if [[ "$QUIET" == 1 ]]; then
if is_quiet "${QUIET}"
then
return
fi
if [[ $2 ]]; then
REPREGION=$2
else
REPREGION=$REGION
fi
CSV_LINE="${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/,/--}"
if [[ "${MODES[@]}" =~ "csv" ]]; then
# Writing csv file replacing commas
echo "${CSV_LINE}" >> ${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
#checking database provider
if [[ ${DATABASE_PROVIDER} == 'postgresql' ]]
then
postgresql_connector "${CSV_LINE}"
fi
general_output "${CHECK_RESULT}" "${CHECK_RESULT_EXTENDED}" "${CHECK_RESOURCE_ID}" "${CHECK_REGION}"
}
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"
CHECK_RESULT='FAIL'
CHECK_RESULT_EXTENDED="${1}"
CHECK_RESOURCE_ID="${3}"
CHECK_REGION="${2}"
# 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 [[ "$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
CSV_LINE="${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/,/--}"
if [[ "${MODES[@]}" =~ "csv" ]]; then
# Writing csv file replacing commas
echo "${CSV_LINE}" >> ${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
#checking database provider
if [[ ${DATABASE_PROVIDER} == 'postgresql' ]]
if [[ "${CHECK_RESULT}" == "FAIL" ]]
then
postgresql_connector "${CSV_LINE}"
set_exitcode
fi
general_output "${CHECK_RESULT}" "${CHECK_RESULT_EXTENDED}" "${CHECK_RESOURCE_ID}" "${CHECK_REGION}"
}
textTitle(){
CHECKS_COUNTER=$((CHECKS_COUNTER+1))
TITLE_ID="$1"
TITLE_ID="${1}"
TITLE_TEXT="${2}"
CHECK_ASFF_COMP_TYPE="${6}"
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
case "${CHECK_ASFF_COMP_TYPE}" 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
echo -e "$TITLE_ID $TITLE_TEXT - $CHECK_SERVICENAME $CHECK_SEVERITY"
}
generateJsonOutput(){
@@ -330,7 +275,7 @@ generateJsonOutput(){
--arg SCORED "$ITEM_SCORED" \
--arg ITEM_CIS_LEVEL "$ITEM_CIS_LEVEL" \
--arg TITLE_ID "$TITLE_ID" \
--arg REPREGION "$REPREGION" \
--arg REGION_FROM_CHECK "$REGION_FROM_CHECK" \
--arg TYPE "$CHECK_ASFF_COMPLIANCE_TYPE" \
--arg TIMESTAMP "$(get_iso8601_timestamp)" \
--arg SERVICENAME "$CHECK_SERVICENAME" \
@@ -354,7 +299,7 @@ generateJsonOutput(){
"Scored": $SCORED,
"Level": $ITEM_CIS_LEVEL,
"Control ID": $TITLE_ID,
"Region": $REPREGION,
"Region": $REGION_FROM_CHECK,
"Timestamp": $TIMESTAMP,
"Compliance": $TYPE,
"Service": $SERVICENAME,
@@ -399,15 +344,15 @@ generateJsonAsffOutput(){
--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 REGION_FROM_CHECK "$REGION_FROM_CHECK" \
--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",
"Id": "prowler-\($TITLE_ID)-\($ACCOUNT_NUM)-\($REGION_FROM_CHECK)-\($UNIQUE_ID)",
"ProductArn": "arn:\($AWS_PARTITION):securityhub:\($REGION_FROM_CHECK)::product/prowler/prowler",
"RecordState": "ACTIVE",
"ProductFields": {
"ProviderName": "Prowler",
@@ -432,7 +377,7 @@ generateJsonAsffOutput(){
"Type": $RESOURCE_TYPE,
"Id": $CHECK_RESOURCE_ID,
"Partition": $AWS_PARTITION,
"Region": $REPREGION
"Region": $REGION_FROM_CHECK
}
],
"Compliance": {
@@ -466,7 +411,7 @@ generateHtmlOutput(){
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>'$REGION_FROM_CHECK'</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
+57 -2
View File
@@ -41,6 +41,7 @@ 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=""
@@ -309,8 +310,7 @@ unset AWS_DEFAULT_OUTPUT
. $PROWLER_DIR/include/db_connector
#Parsing input options
# Parses the check file into CHECK_ID's.
if [[ -n "$CHECK_FILE" ]]; then
if [[ -f $CHECK_FILE ]]; then
@@ -369,11 +369,66 @@ then
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"
+5 -5
View File
@@ -169,14 +169,14 @@ unset HTML_REPORT_INIT
}
addHtmlHeader > ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
while IFS=, read -r PROFILE ACCOUNT_NUM REPREGION TITLE_ID RESULT SCORED LEVEL TITLE_TEXT NOTES ASFF_COMPLIANCE_TYPE CHECK_SEVERITY CHECK_SERVICENAME;do
while IFS=, read -r PROFILE ACCOUNT_NUM REGION_FROM_CHECK TITLE_ID RESULT SCORED LEVEL TITLE_TEXT NOTES ASFF_COMPLIANCE_TYPE CHECK_SEVERITY CHECK_SERVICENAME;do
if [[ $RESULT == "INFO" ]]; then
echo '<tr class="table-info">' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
echo '<td><i class="fas fa-info-circle"></i></td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
echo '<td>INFO</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>'$REGION_FROM_CHECK'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
echo '<td>'$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
@@ -190,7 +190,7 @@ while IFS=, read -r PROFILE ACCOUNT_NUM REPREGION TITLE_ID RESULT SCORED LEVEL T
echo '<td>PASS</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>'$REGION_FROM_CHECK'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
echo '<td>'$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
@@ -204,7 +204,7 @@ while IFS=, read -r PROFILE ACCOUNT_NUM REPREGION TITLE_ID RESULT SCORED LEVEL T
echo '<td>FAIL</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>'$REGION_FROM_CHECK'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
echo '<td>'$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
@@ -218,7 +218,7 @@ while IFS=, read -r PROFILE ACCOUNT_NUM REPREGION TITLE_ID RESULT SCORED LEVEL T
echo '<td>WARN</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>'$REGION_FROM_CHECK'</td>' >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML
echo '<td>'$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