#!/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. # Checks that the correct mode (json-asff) has been specified if wanting to send check output to AWS Security Hub # and that Security Hub is enabled in the chosen region checkSecurityHubCompatibility(){ local regx if [[ "${MODE}" != "json-asff" ]]; then echo -e "\n$RED ERROR!$NORMAL Output can only be sent to Security Hub when the output mode is json-asff, i.e. -M json-asff -S\n" EXITCODE=1 exit $EXITCODE fi for regx in $REGIONS; do SECURITY_HUB_ENABLED=$($AWSCLI securityhub --region "$regx" $PROFILE_OPT describe-hub) if [[ -z "${SECURITY_HUB_ENABLED}" ]]; then echo -e "\n$RED ERROR!$NORMAL Security Hub is not enabled in $regx. Enable it by calling '$AWSCLI securityhub --region $regx $PROFILE_OPT enable-security-hub'\n" EXITCODE=1 exit $EXITCODE fi done # Get unresolved findings SECURITY_HUB_PREVIOUS_FINDINGS=$($AWSCLI securityhub --region "$regx" $PROFILE_OPT get-findings --filters '{"GeneratorId":[{"Value": "prowler-","Comparison":"PREFIX"}],"WorkflowStatus":[{"Value": "RESOLVED","Comparison":"NOT_EQUALS"}]}' | jq -r ".Findings[] | {Id, GeneratorId, Workflow, Compliance}"| jq -cs) } resolveSecurityHubPreviousFails(){ # Move previous findings to Workflow to RESOLVED (as prowler didn't re-detect them) PREVIOUS_FAILED_IDS=$(echo $SECURITY_HUB_PREVIOUS_FINDINGS | jq -c --arg parn "$product_arn" '.[] | select(.Compliance.Status==FAILED) | map({"Id": .Id, ProductArn: $parn} )'); BATCH_UPDATE_RESULT=$($AWSCLI securityhub --region "$region" $PROFILE_OPT batch-update-findings --finding-identifiers "${PREVIOUS_FAILED_IDS}" --workflow '{"Status": "RESOLVED"}') # Check for success if updated if [[ ! -z "${BATCH_UPDATE_RESULT}" ]] && ! jq -e '.ProcessedFindings >= 1' <<< "${BATCH_UPDATE_RESULT}" > /dev/null 2>&1; then echo -e "\n$RED ERROR!$NORMAL Failed to update AWS Security Hub finding\n" fi } sendToSecurityHub(){ local findings="$1" local region="$2" local finding_id=$(echo $findings | jq -r ".Id") local status=$(echo $findings | jq -r ".Compliance.Status") local product_arn=$(echo $findings | jq -r ".ProductArn") local generator_id=$(echo $findings | jq -r ".GeneratorId") PREVIOUS_FINDING=$(echo $SECURITY_HUB_PREVIOUS_FINDINGS | jq --arg finding "$generator_id" '.[] | select((.GeneratorId==$finding))' | jq -cs) if [[ $PREVIOUS_FINDING != "[]" ]]; then # Remove from previous findings to update (using generator) SECURITY_HUB_PREVIOUS_FINDINGS=$(echo $SECURITY_HUB_PREVIOUS_FINDINGS | jq -s --arg finding "$generator_id" '[ .[] | select((.GeneratorId!=$finding)) ]') SAME_STATUS=$(echo $PREVIOUS_FINDING | jq --arg status "$status" '.[] | select(.Compliance.Status!=$status)') SUPPRESSED=$(echo $PREVIOUS_FINDING | jq '.[] | select(.Workflow.Status=="SUPPRESSED")') # If are old non-resolved findings with different status, re-import it to update with previous Id if [[ ! -z $SAME_STATUS && -z $SUPPRESSED ]]; then PREVIOUS_FINDING_ID=$(echo $PREVIOUS_FINDING | jq '.[0].Id' ); findings =$(echo $findings | jq --arg previous_id "$PREVIOUS_FINDING_ID" .[0].Id = previous_id) BATCH_IMPORT_RESULT=$($AWSCLI securityhub --region "$region" $PROFILE_OPT batch-import-findings --findings "${findings}") else PREVIOUS_FINDING_IDS=$(echo $PREVIOUS_FINDING | jq -c --arg parn "$product_arn" 'map({"Id": .Id, ProductArn: $parn} )'); # Update to avoid being deleted after 90 dayss BATCH_UPDATE_RESULT=$($AWSCLI securityhub --region "$region" $PROFILE_OPT batch-update-findings --finding-identifiers "${PREVIOUS_FINDING_IDS}" --note '{"Text": "Finding re-detected by Prowler scan", "UpdatedBy": "prowler"}') fi else #If new, import it BATCH_IMPORT_RESULT=$($AWSCLI securityhub --region "$region" $PROFILE_OPT batch-import-findings --findings "${findings}") fi # Check for success if updated if [[ ! -z "${BATCH_UPDATE_RESULT}" ]] && ! jq -e '.ProcessedFindings >= 1' <<< "${BATCH_UPDATE_RESULT}" > /dev/null 2>&1; then echo -e "\n$RED ERROR!$NORMAL Failed to update AWS Security Hub finding\n" fi # Check for success if imported if [[ ! -z "${BATCH_IMPORT_RESULT}" ]] && ! jq -e '.SuccessCount == 1' <<< "${BATCH_IMPORT_RESULT}" > /dev/null 2>&1; then echo -e "\n$RED ERROR!$NORMAL Failed to send check output to AWS Security Hub\n" fi }