From 2a4cebaa1e1c63c4bfb4f6ac04af167510411287 Mon Sep 17 00:00:00 2001 From: Joaquin Rinaudo Date: Tue, 1 Sep 2020 17:03:25 +0200 Subject: [PATCH] WIP: security hub integration --- include/outputs | 9 ++--- include/securityhub_integration | 58 ++++++++++++++++++++++++++++++--- prowler | 5 +++ 3 files changed, 64 insertions(+), 8 deletions(-) diff --git a/include/outputs b/include/outputs index d4ddba7b..c83c2abd 100644 --- a/include/outputs +++ b/include/outputs @@ -256,6 +256,7 @@ generateJsonAsffOutput(){ local severity=$3 jq -M -c \ + --arg UUID $(uuidgen | awk '{print tolower($0)}') \ --arg ACCOUNT_NUM "$ACCOUNT_NUM" \ --arg TITLE_TEXT "$TITLE_TEXT" \ --arg MESSAGE "$(echo -e "${message}" | sed -e 's/^[[:space:]]*//')" \ @@ -269,15 +270,15 @@ generateJsonAsffOutput(){ --arg TIMESTAMP "$(get_iso8601_timestamp)" \ --arg PROWLER_VERSION "$PROWLER_VERSION" \ --arg AWS_PARTITION "$AWS_PARTITION" \ --n '{ + -n '{ "SchemaVersion": "2018-10-08", - "Id": "prowler-\($TITLE_ID)-\($ACCOUNT_NUM)-\($REPREGION)-\($UNIQUE_ID)", + "Id": "arn:\($AWS_PARTITION):securityhub:\($REPREGION):\($ACCOUNT_NUM):product/prowler/\($PROWLER_VERSION)/finding/\($UUID)", "ProductArn": "arn:\($AWS_PARTITION):securityhub:\($REPREGION):\($ACCOUNT_NUM):product/\($ACCOUNT_NUM)/default", "ProductFields": { "ProviderName": "Prowler", "ProviderVersion": $PROWLER_VERSION }, - "GeneratorId": "prowler-\($PROWLER_VERSION)", + "GeneratorId": "prowler-\($TITLE_ID)-\($ACCOUNT_NUM)-\($REPREGION)-\($UNIQUE_ID)", "AwsAccountId": $ACCOUNT_NUM, "Types": [ $TYPE @@ -294,7 +295,7 @@ generateJsonAsffOutput(){ { "Type": $RESOURCE_TYPE, "Id": "AWS::::Account:\($ACCOUNT_NUM)", - "Partition": $AWS_PARTITION, + "Partition": "aws", "Region": $REPREGION } ], diff --git a/include/securityhub_integration b/include/securityhub_integration index 9c36264b..e1d951e3 100644 --- a/include/securityhub_integration +++ b/include/securityhub_integration @@ -28,15 +28,65 @@ checkSecurityHubCompatibility(){ exit $EXITCODE fi done + # Get unresolved findings, == FAIL as PASSED are moved to RESOLVED. + SECURITY_HUB_PREVIOUS_FINDINGS=$($AWSCLI securityhub 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) + echo "TEST" + 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" - BATCH_IMPORT_RESULT=$($AWSCLI securityhub --region "$region" $PROFILE_OPT batch-import-findings --findings "${findings}") - # A successful CLI response is: {"SuccessCount": 1,"FailedFindings": [],"FailedCount": 0} - # Therefore, check that SuccessCount is indeed 1 - if [[ -z "${BATCH_IMPORT_RESULT}" ]] || ! jq -e '.SuccessCount == 1' <<< "${BATCH_IMPORT_RESULT}" > /dev/null 2>&1; then + 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 [[ $CHECK_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 + echo "Reimport" + 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 + # Update to avoid being deleted after 90 dayss + echo "Comment" + # 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 + echo "Import" + #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 + } diff --git a/prowler b/prowler index e4af677a..d521accb 100755 --- a/prowler +++ b/prowler @@ -559,6 +559,11 @@ if [[ $CHECK_ID ]];then fi execute_all + +if [[ "$SEND_TO_SECURITY_HUB" -eq 1 ]]; then + resolveSecurityHubPreviousFails +fi + if [[ "${MODES[@]}" =~ "html" ]]; then addHtmlFooter >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML fi