diff --git a/README.md b/README.md index 14049803..3ec882c9 100644 --- a/README.md +++ b/README.md @@ -159,7 +159,25 @@ This script has been written in bash using AWS-CLI and it works in Linux and OSX Valid check numbers are based on the AWS CIS Benchmark guide, so 1.1 is check11 and 3.10 is check310 -1. If you want to save your report for later analysis: +### Save your reports + +1. If you want to save your report for later analysis thare are different ways, natively (supported text, mono, csv, json and json-asff see note below for more info): + + ```sh + ./prowler -M csv + ``` + or with multiple formats at the same time: + ```sh + ./prowler -M csv,json,json-asff + ``` + or just a group of checks in multiple formats: + ```sh + ./prowler -g gdpr -M csv,json,json-asff + ``` + + Now `-M` creates a file inside the prowler root directory named `prowler-output-YYYYMMDDHHMMSS.format`. You don't have to specify anything else, no pipes, no redirects. + + or just saving the output to a file like below: ```sh ./prowler -M mono > prowler-report.txt @@ -172,18 +190,9 @@ This script has been written in bash using AWS-CLI and it works in Linux and OSX ./prowler | ansi2html -la > report.html ``` - or if you want a pipe-delimited report file, do: + >Note about output formats to use with `-M`: "text" is the default one with colors, "mono" is like default one but monochrome, "csv" is comma separated values, "json" plain basic json (without comma between lines) and "json-asff" is also json with Amazon Security Finding Format that you can ship to Security Hub using `-S`. - ```sh - ./prowler -M csv > output.psv - ``` - or json formatted output using jq, do: - - ```sh - ./prowler -M json > prowler-output.json - ``` - - or save your report in a S3 bucket: + or save your report in a S3 bucket (this only works for text or mono, for csv, json or json-asff it has to be copied afterwards): ```sh ./prowler -M mono | aws s3 cp - s3://bucket-name/prowler-report.txt @@ -221,7 +230,8 @@ This script has been written in bash using AWS-CLI and it works in Linux and OSX -f specify an AWS region to run checks against (i.e.: us-west-1) -m specify the maximum number of items to return for long-running requests (default: 100) - -M output mode: text (default), mono, json, csv (separator is ,; data is on stdout; progress on stderr) + -M output mode: text (default), mono, json, json-asff, csv. They can be used combined comma separated. + (separator is ","; data is on stdout; progress on stderr). -k keep the credential report -n show check numbers to sort easier (i.e.: 1.01 instead of 1.1) @@ -280,7 +290,7 @@ In order to remove noise and get only FAIL findings there is a `-q` flag that ma Since version v2.3, Prowler supports natively sending findings to [AWS Security Hub](https://aws.amazon.com/security-hub). This integration allows Prowler to import its findings to AWS Security Hub. With Security Hub, you now have a single place that aggregates, organizes, and prioritizes your security alerts, or findings, from multiple AWS services, such as Amazon GuardDuty, Amazon Inspector, Amazon Macie, AWS Identity and Access Management (IAM) Access Analyzer, and AWS Firewall Manager, as well as from AWS Partner solutions and now from Prowler. It is as simple as running the commanbd below: - ```sh + ``` ./prowler -M json-asff -S ``` There are two requirements: diff --git a/include/colors b/include/colors index e938d143..2ae6f77f 100644 --- a/include/colors +++ b/include/colors @@ -11,13 +11,15 @@ # CONDITIONS OF ANY KIND, either express or implied. See the License for the # specific language governing permissions and limitations under the License. -if [[ "$MODE" != "mono" && "$MODE" != "text" && "$MODE" != "csv" && "$MODE" != "json" && "$MODE" != "json-asff" ]]; then - echo "" - echo "$OPTRED ERROR!$OPTNORMAL Invalid output mode. Choose text, mono, csv, json or json-asff." - usage - EXITCODE=1 - exit $EXITCODE -fi + +IFS=',' read -ra MODES <<< "${MODE}" +for MODE in "${MODES[@]}"; do + if [[ "$MODE" != "mono" && "$MODE" != "text" && "$MODE" != "csv" && "$MODE" != "json" && "$MODE" != "json-asff" ]]; then + echo -e "${OPTRED}ERROR!$OPTNORMAL Invalid output mode. Choose text, mono, csv, json or json-asff. ./prowler -h for help" + EXITCODE=1 + exit $EXITCODE + fi +done if [[ "$MODE" == "mono" || "$MODE" == "csv" || "$MODE" == "json" || "$MODE" == "json-asff" ]]; then MONOCHROME=1 diff --git a/include/outputs b/include/outputs index 40d67a7c..1bd93943 100644 --- a/include/outputs +++ b/include/outputs @@ -12,25 +12,36 @@ # specific language governing permissions and limitations under the License. # Output formatting functions + +EXTENSION_CSV="csv" +EXTENSION_JSON="json" +EXTENSION_ASFF="asff-json" +EXTENSION_HTML="html" # not implemented yet, use ansi2html as in documentation +OUTPUT_DATE=$(date -u +"%Y%m%d%H%M%S") +OUTPUT_FILE_NAME=prowler-output-$OUTPUT_DATE + + textPass(){ if [[ "$QUIET" == 1 ]]; then return fi PASS_COUNTER=$((PASS_COUNTER+1)) - if [[ "$MODE" == "csv" || "$MODE" == "json" || "$MODE" == "json-asff" ]]; then + if [[ "${MODES[@]}" =~ "csv" || "${MODES[@]}" =~ "json" || "${MODES[@]}" =~ "json-asff" ]]; then if [[ $2 ]]; then REPREGION=$2 else REPREGION=$REGION fi - if [[ "$MODE" == "csv" ]]; then - echo "$PROFILE${SEP}$ACCOUNT_NUM${SEP}$REPREGION${SEP}$TITLE_ID${SEP}PASS${SEP}$ITEM_SCORED${SEP}$ITEM_LEVEL${SEP}$TITLE_TEXT${SEP}$1" - elif [[ "$MODE" == "json" ]]; then - generateJsonOutput "$1" "Pass" - elif [[ "$MODE" == "json-asff" ]]; then + if [[ "${MODES[@]}" =~ "csv" ]]; then + echo "$PROFILE${SEP}$ACCOUNT_NUM${SEP}$REPREGION${SEP}$TITLE_ID${SEP}PASS${SEP}$ITEM_SCORED${SEP}$ITEM_LEVEL${SEP}$TITLE_TEXT${SEP}$1" | tee -a $OUTPUT_FILE_NAME.$EXTENSION_CSV + fi + if [[ "${MODES[@]}" =~ "json" ]]; then + generateJsonOutput "$1" "Pass" | tee -a $OUTPUT_FILE_NAME.$EXTENSION_JSON + fi + if [[ "${MODES[@]}" =~ "json-asff" ]]; then JSON_ASFF_OUTPUT=$(generateJsonAsffOutput "$1" "PASSED" "INFORMATIONAL") - echo "${JSON_ASFF_OUTPUT}" + echo "${JSON_ASFF_OUTPUT}" | tee -a $OUTPUT_FILE_NAME.$EXTENSION_ASFF if [[ "${SEND_TO_SECURITY_HUB}" -eq 1 ]]; then sendToSecurityHub "${JSON_ASFF_OUTPUT}" fi @@ -45,16 +56,17 @@ textInfo(){ return fi - if [[ "$MODE" == "csv" || "$MODE" == "json" || "$MODE" == "json-asff" ]]; then + if [[ "${MODES[@]}" =~ "csv" || "${MODES[@]}" =~ "json" || "${MODES[@]}" =~ "json-asff" ]]; then if [[ $2 ]]; then REPREGION=$2 else REPREGION=$REGION fi - if [[ "$MODE" == "csv" ]]; then - echo "$PROFILE${SEP}$ACCOUNT_NUM${SEP}$REPREGION${SEP}$TITLE_ID${SEP}INFO${SEP}$ITEM_SCORED${SEP}$ITEM_LEVEL${SEP}$TITLE_TEXT${SEP}$1" - elif [[ "$MODE" == "json" ]]; then - generateJsonOutput "$1" "Info" + if [[ "${MODES[@]}" =~ "csv" ]]; then + echo "$PROFILE${SEP}$ACCOUNT_NUM${SEP}$REPREGION${SEP}$TITLE_ID${SEP}INFO${SEP}$ITEM_SCORED${SEP}$ITEM_LEVEL${SEP}$TITLE_TEXT${SEP}$1" | tee -a $OUTPUT_FILE_NAME.$EXTENSION_CSV + fi + if [[ "${MODES[@]}" =~ "json" ]]; then + generateJsonOutput "$1" "Info" | tee -a $OUTPUT_FILE_NAME.$EXTENSION_JSON fi else echo " $NOTICE INFO! $1 $NORMAL" @@ -64,19 +76,21 @@ textInfo(){ textFail(){ FAIL_COUNTER=$((FAIL_COUNTER+1)) EXITCODE=3 - if [[ "$MODE" == "csv" || "$MODE" == "json" || "$MODE" == "json-asff" ]]; then + if [[ "${MODES[@]}" =~ "csv" || "${MODES[@]}" =~ "json" || "${MODES[@]}" =~ "json-asff" ]]; then if [[ $2 ]]; then REPREGION=$2 else REPREGION=$REGION fi - if [[ "$MODE" == "csv" ]]; then - echo "$PROFILE${SEP}$ACCOUNT_NUM${SEP}$REPREGION${SEP}$TITLE_ID${SEP}FAIL${SEP}$ITEM_SCORED${SEP}$ITEM_LEVEL${SEP}$TITLE_TEXT${SEP}$1" - elif [[ "$MODE" == "json" ]]; then - generateJsonOutput "$1" "Fail" - elif [[ "$MODE" == "json-asff" ]]; then + if [[ "${MODES[@]}" =~ "csv" ]]; then + echo "$PROFILE${SEP}$ACCOUNT_NUM${SEP}$REPREGION${SEP}$TITLE_ID${SEP}FAIL${SEP}$ITEM_SCORED${SEP}$ITEM_LEVEL${SEP}$TITLE_TEXT${SEP}$1" | tee -a $OUTPUT_FILE_NAME.$EXTENSION_CSV + fi + if [[ "${MODES[@]}" =~ "json" ]]; then + generateJsonOutput "$1" "Fail" | tee -a $OUTPUT_FILE_NAME.$EXTENSION_JSON + fi + if [[ "${MODES[@]}" =~ "json-asff" ]]; then JSON_ASFF_OUTPUT=$(generateJsonAsffOutput "$1" "FAILED" "HIGH") - echo "${JSON_ASFF_OUTPUT}" + echo "${JSON_ASFF_OUTPUT}" | tee -a $OUTPUT_FILE_NAME.$EXTENSION_ASFF if [[ "${SEND_TO_SECURITY_HUB}" -eq 1 ]]; then sendToSecurityHub "${JSON_ASFF_OUTPUT}" fi @@ -117,9 +131,9 @@ textTitle(){ *) ITEM_LEVEL="Unspecified or Invalid";; esac - if [[ "$MODE" == "csv" ]]; then - >&2 echo "$TITLE_ID $TITLE_TEXT" - elif [[ "$MODE" == "json" || "$MODE" == "json-asff" ]]; then + if [[ "${MODES[@]}" =~ "csv" ]]; then + >&2 echo "$TITLE_ID $TITLE_TEXT" | tee -a $OUTPUT_FILE_NAME.$EXTENSION_CSV + elif [[ "${MODES[@]}" =~ "json" || "${MODES[@]}" =~ "json-asff" ]]; then : else if [[ "$ITEM_SCORED" == "Scored" ]]; then diff --git a/prowler b/prowler index a07474ba..022d1b33 100755 --- a/prowler +++ b/prowler @@ -65,7 +65,8 @@ USAGE: -f specify an AWS region to run checks against (i.e.: us-west-1) -m specify the maximum number of items to return for long-running requests (default: 100) - -M output mode: text (default), mono, json, json-asff, csv (separator is ","; data is on stdout; progress on stderr) + -M output mode: text (default), mono, json, json-asff, csv. They can be used combined comma separated. + (separator is ","; data is on stdout; progress on stderr). -k keep the credential report -n show check numbers to sort easier (i.e.: 1.01 instead of 1.1) @@ -393,7 +394,7 @@ get_all_checks_without_exclusion() { } ### All functions defined above ... run the workflow -if [[ $MODE != "csv" ]]; then +if [[ ${MODES[@]} =~ "mono" || ${MODES[@]} =~ "text" ]]; then prowlerBanner fi @@ -424,9 +425,7 @@ getWhoami # Execute group of checks if called with -g if [[ $GROUP_ID_READ ]];then if [[ " ${GROUP_ID[@]} " =~ " ${GROUP_ID_READ} " ]]; then - if [[ $MODE == "csv" ]]; then - BANNER=0 - fi + execute_group_by_id ${GROUP_ID_READ} ${EXCLUDE_CHECK_ID} cleanTemp scoring