#!/usr/bin/env bash

set -eo pipefail

########################
### GLOBAL VARIABLES ###
########################

# If using a locally built stateless CI container, export ANCHORE_CI_IMAGE=<image_name>. 
# This will override the image name from Dockerhub.
INLINE_SCAN_IMAGE="${ANCHORE_CI_IMAGE:-docker.io/anchore/inline-scan:v0.10.2}"
DOCKER_NAME="${RANDOM:-temp}-inline-anchore-engine"
DOCKER_ID=""
ANALYZE=false
VULN_SCAN=false
CREATE_CMD=()
RUN_CMD=()
COPY_CMDS=()
IMAGE_NAMES=()
IMAGE_FILES=()
SCAN_IMAGES=()
FAILED_IMAGES=()
VALIDATED_OPTIONS=""
# Vuln scan option variable defaults
DOCKERFILE="./Dockerfile"
POLICY_BUNDLE="./policy_bundle.json"
TIMEOUT=600
VOLUME_PATH="/tmp/"
# Analyzer option variable defaults
ANCHORE_URL="http://localhost:8228"
ANCHORE_USER="admin"
ANCHORE_PASS="foobar"
ANCHORE_ANNOTATIONS="foo=bar"
IMAGE_DIGEST_SHA="sha256:123456890abcdefg"
ANCHORE_IMAGE_ID="123456890abcdefg"
MANIFEST_FILE="./manifest.json"

deprecation_notice() {
cat << EOF

**WARNING**

The Anchore Inline Scan script is deprecated and will reach EOL on Jan 10, 2022.
The vulnerability database will no longer be kept up to date and new images will no longer be published.

Please update your integrations to use our new CLI vulnerability scanning tool, grype.

https://github.com/anchore/grype

**WARNING**

EOF
sleep 10
}

display_usage() {
cat << EOF

Anchore Engine Inline Scanner/Analyzer --

  Wrapper script for performing vulnerability scan or image analysis on local docker images, utilizing the Anchore Engine inline_scan container.
  For more detailed usage instructions use the -h option after specifying scan or analyze.

    Usage: ${0##*/} <scan|analyze> [ OPTIONS ]

EOF
}

display_usage_scanner() {
cat << EOF

Anchore Engine Inline Scan --

  Script for performing vulnerability scan on local docker images, utilizing the Anchore Engine inline_scan container.
  Multiple images can be passed for scanning if a Dockerfile is not specified. 

  Images should be built & tagged locally, or remote images can be pulled with the -p option.

    Usage: ${0##*/} [ OPTIONS ] <FULL_IMAGE_TAG_1> <FULL_IMAGE_TAG_2> <....>

      -b <PATH>  [optional] Path to local Anchore policy bundle (ex: -b ./policy_bundle.json)
      -d <PATH>  [optional] Path to local Dockerfile (ex: -d ./dockerfile)
      -t <TEXT>  [optional] Specify timeout for image scanning in seconds. Defaults to 300s. (ex: -t 500)
      -f  [optional] Exit script upon failed Anchore policy evaluation
      -p  [optional] Pull remote docker images
      -r  [optional] Generate analysis reports in your current working directory
      -V  [optional] Increase verbosity

EOF
}

display_usage_analyzer() {
cat << EOF

Anchore Engine Inline Analyzer --

  Script for performing analysis on local docker images, utilizing the Anchore Engine analyzer subsystem.
  After image is analyzed, the resulting Anchore image archive is sent to a remote Anchore Engine installation
  using the -r <URL> option. This allows inline_analysis data to be persisted & utilized for reporting.

  Images should be built & tagged locally.

    Usage: ${0##*/} analyze -r <REMOTE_URL> -u <USER> -p <PASSWORD> [ OPTIONS ] <FULL_IMAGE_TAG>

      -r <TEXT>  [required] URL to remote Anchore Engine API endpoint (ex: -r 'https://anchore.example.com:8228/v1')
      -u <TEXT>  [required] Username for remote Anchore Engine auth (ex: -u 'admin')
      -p <TEXT>  [required] Password for remote Anchore Engine auth (ex: -p 'foobar')

      -a <TEXT>  [optional] Add annotations (ex: -a 'key=value,key=value')
      -d <PATH>  [optional] Specify image digest (ex: -d 'sha256:<64 hex characters>')
      -f <PATH>  [optional] Path to Dockerfile (ex: -f ./Dockerfile)
      -i <TEXT>  [optional] Specify image ID used within Anchore Engine (ex: -i '<64 hex characters>')
      -m <PATH>  [optional] Path to Docker image manifest (ex: -m ./manifest.json)
      -t <TEXT>  [optional] Specify timeout for image analysis in seconds. Defaults to 300s. (ex: -t 500)
      -g  [optional] Generate an image digest from docker save tarball
      -P  [optional] Pull docker image from registry
      -V  [optional] Increase verbosity

EOF
}

main() {
    trap 'cleanup' EXIT ERR SIGTERM 
    trap 'interupt' SIGINT

    if [[ "$#" -lt 1 ]]; then
        deprecation_notice >&2
        display_usage >&2
        printf '\n\t%s\n\n' "ERROR - must specify operation ('scan' or 'analyze')" >&2
        exit 1
    fi
    if [[ "$1" == 'help' ]]; then
        deprecation_notice >&2
        display_usage >&2
	exit 1
    elif [[ "$1" == 'analyze' ]]; then
        shift "$((OPTIND))"
        deprecation_notice >&2
        ANALYZE=true
        get_and_validate_analyzer_options "$@"
        get_and_validate_images "${VALIDATED_OPTIONS}"
        prepare_inline_container
        CREATE_CMD+=('analyze')
        RUN_CMD+=('analyze')
        start_analysis
        deprecation_notice >&2
    else
        if [[ "$1" == 'scan' ]]; then
            shift "$((OPTIND))"
        fi
        deprecation_notice >&2
        VULN_SCAN=true
        get_and_validate_scanner_options "$@"
        get_and_validate_images "${VALIDATED_OPTIONS}"
        prepare_inline_container
        CREATE_CMD+=('scan')
        RUN_CMD+=('scan')
        start_vuln_scan
        deprecation_notice >&2
    fi
}

get_and_validate_analyzer_options() {
    #Parse options
    while getopts ':r:u:p:a:d:f:i:m:t:PgVh' option; do
        case "${option}" in
            r  ) r_flag=true; ANCHORE_URL="${OPTARG%%/v1}";;
            u  ) u_flag=true; ANCHORE_USER="${OPTARG}";;
            p  ) p_flag=true; ANCHORE_PASS="${OPTARG}";;
            a  ) a_flag=true; ANCHORE_ANNOTATIONS="${OPTARG}";;
            d  ) d_flag=true; IMAGE_DIGEST_SHA="${OPTARG}";;
            f  ) f_flag=true; DOCKERFILE="${OPTARG}";;
            i  ) i_flag=true; ANCHORE_IMAGE_ID="${OPTARG}";;
            m  ) m_flag=true; MANIFEST_FILE="${OPTARG}";;
            t  ) t_flag=true; TIMEOUT="${OPTARG}";;
            P  ) P_flag=true;;
            g  ) g_flag=true;;
            V  ) V_flag=true;;
            h  ) display_usage_analyzer; exit;;
            \? ) printf "\n\t%s\n\n" "Invalid option: -${OPTARG}" >&2; display_usage_analyzer >&2; exit 1;;
            :  ) printf "\n\t%s\n\n%s\n\n" "Option -${OPTARG} requires an argument." >&2; display_usage_analyzer >&2; exit 1;;
        esac
    done
    shift "$((OPTIND - 1))"

    # Check for invalid options
    if [[ ! $(which docker) ]]; then
        printf '\n\t%s\n\n' 'ERROR - Docker is not installed or cannot be found in $PATH' >&2
        display_usage_analyzer >&2
        exit 1
    elif [[ "${#@}" -gt 1 ]]; then
        printf '\n\t%s\n\n' "ERROR - only 1 image can be analyzed at a time" >&2
        display_usage_analyzer >&2
        exit 1
    elif [[ "${#@}" -lt 1 ]]; then
        printf '\n\t%s\n\n' "ERROR - must specify an image to analyze" >&2
        display_usage_analyzer >&2
        exit 1
        # validate URL is functional anchore-engine api endpoint
    elif [[ ! "${r_flag}" ]]; then
        printf '\n\t%s\n\n' "ERROR - must provide an anchore-engine endpoint" >&2
        display_usage_analyzer >&2
        exit 1
    elif ! curl -s --fail "${ANCHORE_URL%%/}/v1" > /dev/null; then
        printf '\n\t%s\n\n' "ERROR - invalid anchore-engine endpoint provided - ${ANCHORE_URL}" >&2
        display_usage_analyzer >&2
        exit 1
    # validate user & password are provided & correct
    elif [[ ! "${u_flag}" ]] || [[ ! "${p_flag}" ]]; then
        printf '\n\t%s\n\n' "ERROR - must provide anchore-engine username & password" >&2
        display_usage_analyzer >&2
        exit 1
    elif ! curl -s --fail -u "${ANCHORE_USER}:${ANCHORE_PASS}" "${ANCHORE_URL%%/}/v1/status" > /dev/null; then
        printf '\n\t%s\n\n' "ERROR - invalid anchore-engine username/password provided" >&2
        display_usage_analyzer >&2
        exit 1
    elif [[ "${a_flag}" ]]; then
        # transform all commas to spaces & cast to an array
        local annotation_array=(${ANCHORE_ANNOTATIONS//,/ })
        # get count of = in annotation string
        local number_keys=${ANCHORE_ANNOTATIONS//[^=]}
        # compare number of elements in array with number of = in annotation string
        if [[ "${#number_keys}" -ne "${#annotation_array[@]}" ]]; then
            printf '\n\t%s\n\n' "ERROR - ${ANCHORE_ANNOTATIONS} is not a valid input for -a option" >&2
            display_usage_analyzer >&2
            exit 1
        fi
    elif [[ ! "${g_flag}" ]] && [[ ! "${d_flag}" ]] && [[ ! "${m_flag}" ]]; then
        printf '\n\t%s\n\n' "ERROR - must provide an image digest, manifest, or specify -g to generate a digest" >&2
        display_usage_analyzer >&2
        exit 1
    elif [[ "${g_flag}" ]] && ([[ "${d_flag}" ]] || [[ "${m_flag}" ]]); then
        printf '\n\t%s\n\n' "ERROR - cannot generate digest when a manifest or digest is provided" >&2
        display_usage_analyzer >&2
        exit 1
    elif [[ "${f_flag}" ]] && [[ ! -f "${DOCKERFILE}" ]]; then
        printf '\n\t%s\n\n' "ERROR - Dockerfile: ${DOCKERFILE} does not exist" >&2
        display_usage_analyzer >&2
        exit 1
    elif [[ "${m_flag}" ]] && [[ ! -f "${MANIFEST_FILE}" ]];then
        printf '\n\t%s\n\n' "ERROR - Manifest: ${MANIFEST_FILE} does not exist" >&2
        display_usage_analyzer >&2
        exit 1
    elif [[ "${t_flag}" ]] && [[ ! "${TIMEOUT}" =~ ^[0-9]+$ ]]; then
        printf '\n\t%s\n\n' "ERROR - timeout must be set to a valid integer" >&2
        display_usage_analyzer >&2
        exit 1
    fi

    if [[ "$V_flag" ]]; then
        set -x
    fi

    VALIDATED_OPTIONS="$@"
}

get_and_validate_scanner_options() {
    # Parse options
    while getopts ':d:b:t:fhrVp' option; do
        case "${option}" in
            d  ) d_flag=true; DOCKERFILE="${OPTARG}";;
            f  ) f_flag=true;;
            r  ) r_flag=true;;
            b  ) b_flag=true; POLICY_BUNDLE="${OPTARG}";;
            p  ) p_flag=true;;
            t  ) t_flag=true; TIMEOUT="${OPTARG}";;
            V  ) V_flag=true;;
            h  ) display_usage_scanner; exit;;
            \? ) printf "\n\t%s\n\n" "  Invalid option: -${OPTARG}" >&2; display_usage_scanner >&2; exit 1;;
            :  ) printf "\n\t%s\n\n%s\n\n" "  Option -${OPTARG} requires an argument" >&2; display_usage_scanner >&2; exit 1;;
        esac
    done
    shift "$((OPTIND - 1))"

    # Check for invalid options
    if [[ "${#@}" -lt 1 ]]; then
        printf '\n\t%s\n\n' "ERROR - must specify an image to scan" >&2
        display_usage_scanner >&2
        exit 1
        # validate URL is functional anchore-engine api endpoint
    elif [[ ! $(which docker) ]]; then
        printf '\n\t%s\n\n' 'ERROR - Docker is not installed or cannot be found in $PATH' >&2
        display_usage_scanner >&2
        exit 1
    elif [[ "${d_flag}" ]] && [[ "${#@}" -gt 1 ]]; then
        printf '\n\t%s\n\n' "ERROR - If specifying a Dockerfile, only 1 image can be scanned at a time" >&2
        display_usage_scanner >&2
        exit 1
    elif [[ "${r_flag}" ]] && ! (mkdir -p ./anchore-reports); then
        printf '\n\t%s\n\n' "ERROR - ${PWD}/anchore-reports is not writable" >&2
        display_usage_scanner >&2
        exit 1
    elif [[ "${b_flag}" ]] && [[ ! -f "${POLICY_BUNDLE}" ]]; then
        printf '\n\t%s\n\n' "ERROR - Policy Bundle: ${POLICY_BUNDLE} does not exist" >&2
        display_usage_scanner >&2
        exit 1
    elif [[ "${d_flag}" ]] && [[ ! -f "${DOCKERFILE}" ]]; then
        printf '\n\t%s\n\n' "ERROR - Dockerfile: ${DOCKERFILE} does not exist" >&2
        display_usage_scanner >&2
        exit 1
    elif [[ "${#@}" -eq 0 ]]; then
        printf '\n\t%s\n\n' "ERROR - ${0##*/} requires at least 1 image name as input" >&2
        display_usage_scanner >&2
        exit 1
    elif [[ "${t_flag}" ]] && [[ ! "${TIMEOUT}" =~ ^[0-9]+$ ]]; then
        printf '\n\t%s\n\n' "ERROR - timeout must be set to a valid integer" >&2
        display_usage_scanner >&2
        exit 1
    fi

    if [[ "$V_flag" ]]; then
        set -x
    fi

    VALIDATED_OPTIONS="$@"
}

get_and_validate_images() {
    # Add all unique positional input params to IMAGE_NAMES array
    for i in $@; do
        if [[ ! "${IMAGE_NAMES[@]}" =~ "$i" ]]; then
            IMAGE_NAMES+=("$i")
        fi
    done

    # Make sure all images are available locally, add to FAILED_IMAGES array if not
    for i in "${IMAGE_NAMES[@]}"; do
        if ([[ "${p_flag}" == true ]] && [[ "${VULN_SCAN}" == true ]]) || [[ "${P_flag}" == true ]]; then
            echo "Pulling image -- $i"
            docker pull $i || true
        fi

        docker inspect "$i" &> /dev/null || FAILED_IMAGES+=("$i")

        if [[ ! "${FAILED_IMAGES[@]}" =~ "$i" ]]; then
            SCAN_IMAGES+=("$i")
        fi
    done

    # Give error message on any invalid image names
    if [[ "${#FAILED_IMAGES[@]}" -gt 0 ]]; then
        printf '\n%s\n\n' "WARNING - Please pull remote image, or build/tag all local images before attempting analysis again" >&2

        if [[ "${#FAILED_IMAGES[@]}" -ge "${#IMAGE_NAMES[@]}" ]]; then
            printf '\n\t%s\n\n' "ERROR - no local docker images specified in script input: ${0##*/} ${IMAGE_NAMES[*]}" >&2
            display_usage >&2
            exit 1
        fi

        for i in "${FAILED_IMAGES[@]}"; do
            printf '\t%s\n' "Could not find image locally -- $i" >&2
        done
    fi
}

prepare_inline_container() {
    # Check if env var is overriding which inline-scan image to utilize.
    if [[ -z "${ANCHORE_CI_IMAGE}" ]]; then
        printf '\n%s\n' "Pulling ${INLINE_SCAN_IMAGE}"
        docker pull "${INLINE_SCAN_IMAGE}"
    else
        printf '\n%s\n' "Using local image for scanning -- ${INLINE_SCAN_IMAGE}"
    fi

    # setup command arrays to eval & run after adding all required options
    CREATE_CMD=('docker create --name "${DOCKER_NAME}"')
    RUN_CMD=('docker run -i --name "${DOCKER_NAME}"')

    if [[ "${t_flag}" ]]; then
        CREATE_CMD+=('-e TIMEOUT="${TIMEOUT}"')
        RUN_CMD+=('-e TIMEOUT="${TIMEOUT}"')
    fi
    if [[ "${V_flag}" ]]; then
        CREATE_CMD+=('-e VERBOSE=true')
        RUN_CMD+=('-e VERBOSE=true')
    fi

    CREATE_CMD+=('"${INLINE_SCAN_IMAGE}"')
    RUN_CMD+=('"${INLINE_SCAN_IMAGE}"')
}

start_vuln_scan() {
    if [[ "${f_flag}" ]]; then
        CREATE_CMD+=('-f')
        RUN_CMD+=('-f')
    fi
    if [[ "${r_flag}" ]]; then
        CREATE_CMD+=('-r')
        RUN_CMD+=('-r')
    fi

    # If no files need to be copied to container, pipe docker save output to stdin of docker run command.
    if [[ ! "${d_flag}" ]] && [[ ! "${b_flag}" ]] && [[ "${#SCAN_IMAGES[@]}" -eq 1 ]]; then
        RUN_CMD+=('-i "${SCAN_IMAGES[*]}"')

        # If image is passed without a tag, append :latest to docker save to prevent skopeo manifest error
        if [[ ! "${SCAN_IMAGES[*]}" =~ [:]+ ]]; then
            docker save "${SCAN_IMAGES[*]}:latest" | eval "${RUN_CMD[*]}"
        else
            docker save "${SCAN_IMAGES[*]}" | eval "${RUN_CMD[*]}"
        fi
    else
        # Prepare commands for container creation & copying all files to container.
        if [[ "${b_flag}" ]]; then
            CREATE_CMD+=('-b "${POLICY_BUNDLE}"')
            COPY_CMDS+=('docker cp "${POLICY_BUNDLE}" "${DOCKER_NAME}:/anchore-engine/$(basename ${POLICY_BUNDLE})";')
        fi
        if [[ "${#SCAN_IMAGES[@]}" -eq 1 ]]; then
	    if [[ "${d_flag}" ]]; then
                CREATE_CMD+=('-d "${DOCKERFILE}" -i "${SCAN_IMAGES[*]}"')
                COPY_CMDS+=('docker cp "${DOCKERFILE}" "${DOCKER_NAME}:/anchore-engine/$(basename ${DOCKERFILE})";')
	    else
	        CREATE_CMD+=('-i "${SCAN_IMAGES[*]}"')
	    fi
        fi

        DOCKER_ID=$(eval "${CREATE_CMD[*]}")
        eval "${COPY_CMDS[*]}"
        save_and_copy_images
        echo
        docker start -ia "${DOCKER_NAME}"
    fi
}

start_analysis() {
    # Prepare commands for container creation & copying all files to container.
    if [[ "${d_flag}" ]]; then
        CREATE_CMD+=('-d "${IMAGE_DIGEST_SHA}"')
    fi
    if [[ "${i_flag}" ]]; then
        CREATE_CMD+=('-i "${ANCHORE_IMAGE_ID}"')
    fi
    if [[ "${a_flag}" ]]; then
        CREATE_CMD+=('-a "${ANCHORE_ANNOTATIONS}"')
    fi
    if [[ "${g_flag}" ]]; then
        CREATE_CMD+=('-g')
    fi
    if [[ "${m_flag}" ]]; then
        CREATE_CMD+=('-m "${MANIFEST_FILE}"')
        COPY_CMDS+=('docker cp "${MANIFEST_FILE}" "${DOCKER_NAME}:/anchore-engine/$(basename ${MANIFEST_FILE})";')
    fi
    if [[ "$f_flag" ]]; then
        CREATE_CMD+=('-f "${DOCKERFILE}"')
        COPY_CMDS+=('docker cp "${DOCKERFILE}" "${DOCKER_NAME}:/anchore-engine/$(basename ${DOCKERFILE})";')
    fi

    # finally, get the account from anchore engine for the input username
    mkdir -p /tmp/anchore
    HCODE=$(curl -sS --output /tmp/anchore/anchore_output.log --write-out "%{http_code}" -u "${ANCHORE_USER}:${ANCHORE_PASS}" "${ANCHORE_URL%%/}/v1/account")
    if [[ "${HCODE}" == 200 ]] && [[ -f "/tmp/anchore/anchore_output.log" ]]; then
        ANCHORE_ACCOUNT=$(cat /tmp/anchore/anchore_output.log | grep '"name"' | awk -F'"' '{print $4}')
        CREATE_CMD+=('-u "${ANCHORE_ACCOUNT}"')
    else
        printf '\n\t%s\n\n' "ERROR - unable to fetch account information from anchore-engine for specified user"
        if [ -f /tmp/anchore/anchore_output.log ]; then
            printf '%s\n\n' "***SERVICE RESPONSE****">&2
            cat /tmp/anchore/anchore_output.log >&2
            printf '\n%s\n' "***END SERVICE RESPONSE****" >&2
        fi
	    exit 1
    fi

    CREATE_CMD+=("${SCAN_IMAGES[*]}")
    DOCKER_ID=$(eval "${CREATE_CMD[*]}")
    eval "${COPY_CMDS[*]}"
    save_and_copy_images
    echo
    docker start -ia "${DOCKER_NAME}"
    
    local analysis_archive_name="${IMAGE_FILES[*]%.tar}-archive.tgz"
    # copy image analysis archive from inline_scan containter to host & curl to remote anchore-engine endpoint
    docker cp "${DOCKER_NAME}:/anchore-engine/image-analysis-archive.tgz" "/tmp/anchore/${analysis_archive_name}"
    
    if [[ -f "/tmp/anchore/${analysis_archive_name}" ]]; then
        printf '%s\n' " Analysis complete!"
        printf '\n%s\n' "Sending analysis archive to ${ANCHORE_URL%%/}"
        HCODE=$(curl -sS --output /tmp/anchore/anchore_output.log --write-out "%{http_code}" -u "${ANCHORE_USER}:${ANCHORE_PASS}" -F "archive_file=@/tmp/anchore/${analysis_archive_name}" "${ANCHORE_URL%%/}/v1/import/images")
        if [[ "${HCODE}" != 200 ]]; then
            printf '\n\t%s\n\n' "ERROR - unable to POST ${analysis_archive_name} to ${ANCHORE_URL%%/}/v1/import/images" >&2
            if [ -f /tmp/anchore/anchore_output.log ]; then
                printf '%s\n\n' "***SERVICE RESPONSE****">&2
                cat /tmp/anchore/anchore_output.log >&2
                printf '\n%s\n' "***END SERVICE RESPONSE****" >&2
            fi
            exit 1
        else
            if [ -f /tmp/anchore/anchore_output.log ]; then
                cat /tmp/anchore/anchore_output.log
            fi
        fi
    else
        printf '\n\t%s\n\n' "ERROR - analysis file invalid: /tmp/anchore/${analysis_archive_name}. An error occured during analysis."  >&2
        display_usage_analyzer >&2
        exit 1
    fi
}

save_and_copy_images() {
    # Save all image files to /tmp and copy to created container
    for image in "${SCAN_IMAGES[@]}"; do
        local base_image_name="${image##*/}"
        echo "Saving ${image} for local analysis"
        local save_file_name="${base_image_name}.tar"
        # IMAGE_FILES is used for storing temp image paths for cleanup at end of script
        IMAGE_FILES+=("$save_file_name")
        
        mkdir -p /tmp/anchore
        local save_file_path="/tmp/anchore/${save_file_name}"

        # If image is passed without a tag, append :latest to docker save to prevent skopeo manifest error
        if [[ ! "${image}" =~ [:]+ ]]; then
            docker save "${image}:latest" -o "${save_file_path}"
        else
            docker save "${image}" -o "${save_file_path}"
        fi

        if [[ -f "${save_file_path}" ]]; then
            chmod +r "${save_file_path}"
            printf '%s' "Successfully prepared image archive -- ${save_file_path}"
        else
            printf '\n\t%s\n\n' "ERROR - unable to save docker image to ${save_file_path}." >&2
            display_usage >&2
            exit 1
        fi

        docker cp "${save_file_path}" "${DOCKER_NAME}:/anchore-engine/${save_file_name}"
        rm -f "${save_file_path}"
    done
}

interupt() {
    cleanup 130
}

cleanup() {
    local ret="$?"
    if [[ "${#@}" -ge 1 ]]; then
        local ret="$1"
    fi
    set +e

    if [[ -z "${DOCKER_ID}" ]]; then
        DOCKER_ID="${DOCKER_NAME:-$(docker ps -a | grep 'inline-anchore-engine' | awk '{print $1}')}"
    fi

    for id in ${DOCKER_ID}; do
        local -i timeout=0
        while (docker ps -a | grep "${id:0:10}") > /dev/null && [[ "${timeout}" -lt 12 ]]; do
            if [[ "${r_flag}" ]]; then
                echo "Copying scan reports from ${DOCKER_NAME} to ${PWD}/anchore-reports/"
                docker cp "${DOCKER_NAME}:/anchore-engine/anchore-reports/" ./
            fi
            docker kill "${id}" &> /dev/null
            docker rm "${id}" &> /dev/null
            printf '\n%s\n' "Cleaning up docker container: ${id}"
            ((timeout=timeout+1))
            sleep 5
        done

        if [[ "${timeout}" -ge 12 ]]; then
            exit 1
        fi
        unset DOCKER_ID
    done
    
    if [[ "${#IMAGE_FILES[@]}" -ge 1 ]] || [[ -f /tmp/anchore/anchore_output.log ]]; then
        if [[ -d "/tmp/anchore" ]]; then
            rm -rf "/tmp/anchore"
        fi
    fi

    exit "${ret}"
}

main "$@"
