github.com/verrazzano/verrazzano@v1.7.0/release/scripts/scan_bom_images.sh (about)

     1  #!/usr/bin/env bash
     2  #
     3  # Copyright (c) 2021, 2023, Oracle and/or its affiliates.
     4  # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     5  #
     6  # Scan images in a specified BOM
     7  
     8  SCRIPT_DIR=$(cd $(dirname "$0"); pwd -P)
     9  TOOL_SCRIPT_DIR=${SCRIPT_DIR}/../../tools/scripts
    10  
    11  . $SCRIPT_DIR/common.sh
    12  
    13  ALLOW_LIST=
    14  BOM_FILE=
    15  REGISTRY=
    16  REPO_PATH=
    17  OUTPUT_DIR=
    18  OCIR_NAMESPACE=
    19  
    20  # The COMBINED_PATH is formed based on whether there is an OCIR tenancy namespace or not:
    21  #       $REGISTRY/$REPO_PATH
    22  #       $REGISTRY/$OCIR_NAMESPACE/$REPO_PATH
    23  COMBINED_PATH=
    24  
    25  function usage() {
    26    ec=${1:-0}
    27    if [ ! -z "$2" ]; then
    28      echo "$2"
    29    fi
    30  
    31    cat << EOM
    32    Scan images in specified BOM
    33  
    34    Usage:
    35      $(basename $0) -b bom-file -r results-directory [-a allow-list]
    36  
    37    Options:
    38      -b <bom-file>           BOM file to use for determining images to scan (required)
    39      -o <output-directory>   Directory to store scan result files (required)
    40      -r <docker-registry>    Docker registry to find images in (required)
    41      -n <ocir-namespace>     OCIR namespace. This is required if the registry is an OCIR registry, and is not needed otherwise (optional)
    42      -p <repository-path>    Repository name/prefix for each image, e.g \"path/to/my/image\"; not including an OCIR namespace if one is required, if not specified the default will be used according to the BOM
    43      -x <ocir-repo-path>     This is the OCIR namespace plus the repository path as well (basically -n and -p combined).
    44      -a <allow-list>         Allow-list to use for scanning (optional)
    45  
    46      One of the path options must be specified: -x or -p
    47  
    48    Pre-requisites:
    49      This requires that trivy and grype scanners are installed
    50  
    51    Example:
    52      $(basename $0) -b verrazzano-bom.json -t ghcr.io -p my/repo/path -o scan-results
    53      $(basename $0) -b verrazzano-bom.json -t phx.ocir.io -n tenancynamespace -p my/repo/path -o scan-results
    54  EOM
    55    exit ${ec}
    56  }
    57  
    58  # This will get the scan summaries and details for all of the repositories
    59  #
    60  # It will also verify that all repositories found have scan results as well
    61  #
    62  # $1 Scan BOM file
    63  function scan_images_in_bom() {
    64    # Get a list of the images in the BOM based on the specified registry and repo path, but trim the paths
    65    # off in the file so we are left with them in the form image:tag
    66    local bomimages=$(mktemp temp-bom-images-XXXXXX.out)
    67    sh $TOOL_SCRIPT_DIR/vz-registry-image-helper.sh -m $bomimages -t $REGISTRY -r $COMBINED_PATH -b $BOM_FILE
    68    cat $bomimages
    69    echo "$REGISTRY/$COMBINED_PATH"
    70    sed -i "s;$REGISTRY/$COMBINED_PATH/;;g" $bomimages
    71  
    72    cat $bomimages
    73  
    74    # Scan each image listed in the BOM
    75    while read BOM_IMAGE; do
    76      RESULT_REPOSITORY_IMAGE=$(echo "${BOM_IMAGE}" | sed 's;/;_;g')
    77      RESULT_FILE_PREFIX=$(echo "${OUTPUT_DIR}/${RESULT_REPOSITORY_IMAGE}")
    78      SCAN_IMAGE=$(echo "${REGISTRY}/${COMBINED_PATH}/${BOM_IMAGE}")
    79  
    80      # FIXME: Add allowlist support
    81  
    82      echo "performing trivy scan for $SCAN_IMAGE"
    83      trivy image -f json -o ${RESULT_FILE_PREFIX}-trivy-details.json ${SCAN_IMAGE} 2> ${RESULT_FILE_PREFIX}-trivy.err || echo "trivy scan failed for $SCAN_IMAGE"
    84      # Check if any Results section was included or not, only convert to CSV if there are Results there
    85      results_size=$(cat ${RESULT_FILE_PREFIX}-trivy-details.json |  jq '.Results' | wc -l)
    86      if [[ results_size -gt 1 ]]; then
    87        cat ${RESULT_FILE_PREFIX}-trivy-details.json | jq -r '.Results[]' | sed 's/null//g' | jq -r '.Vulnerabilities' | sed 's/null//g' | jq -r ' .[] | { sev: .Severity, cve: ."VulnerabilityID", description: .Description } ' | sed 's/\\[nt]/ /g' | jq -r '[.[]] | @csv' | sort -u > ${RESULT_FILE_PREFIX}-trivy-details.csv
    88      else
    89        echo "No Results section found in ${RESULT_FILE_PREFIX}-trivy-details.json, so no need to generate CSV for it"
    90      fi
    91  
    92      echo "performing grype scan for $BOM_IMAGE"
    93      grype ${SCAN_IMAGE} -o json > ${RESULT_FILE_PREFIX}-grype-details.json 2> ${RESULT_FILE_PREFIX}-grype.err || echo "grype scan failed for $SCAN_IMAGE"
    94      cat ${RESULT_FILE_PREFIX}-grype-details.json | jq -r '.matches[] | { sev: .vulnerability.severity, cve: .vulnerability.id, description: .vulnerability.description } ' | sed 's/\\[nt]/ /g' | jq -r '[.[]] | @csv' | sort -u > ${RESULT_FILE_PREFIX}-grype-details.csv
    95    done <$bomimages
    96    rm $bomimages
    97  }
    98  
    99  function validate_inputs() {
   100    if [ -z "$BOM_FILE" ] || [ ! -f "$BOM_FILE" ]; then
   101      usage 1 "A valid BOM file is required to be specified"
   102    fi
   103  
   104    if [ -z "$OUTPUT_DIR" ]; then
   105      usage 1 "A valid scan results directory is required to be specified"
   106    else
   107      if [ ! -d "$OUTPUT_DIR" ]; then
   108        mkdir -p $OUTPUT_DIR || usage 1 "Failed creating $OUTPUT_DIR. A valid scan results directory location is required to be specified"
   109      fi
   110    fi
   111  
   112    if [ -z "$REGISTRY" ]; then
   113      usage 1 "The registry must be specified"
   114    fi
   115  
   116    if [ -z "$REPO_PATH" ]; then
   117      usage 1 "The repository path must be specified"
   118    fi
   119  
   120    # If we already have a combined path, we were give -x with a namespace and path already
   121    # If not, then we check if they gave us -p or -n and -p
   122    if [ -z "$COMBINED_PATH" ]; then
   123      if [ -z "$OCIR_NAMESPACE" ]; then
   124        COMBINED_PATH="$REPO_PATH"
   125      else
   126        COMBINED_PATH="$OCIR_NAMESPACE/$REPO_PATH"
   127      fi
   128    fi
   129  }
   130  
   131  function verify_prerequisites() {
   132    command -v trivy >/dev/null 2>&1 || {
   133      usage 1 "trivy scanner is not installed"
   134    }
   135    command -v grype >/dev/null 2>&1 || {
   136      usage 1 "grype scanner is not installed"
   137    }
   138    echo "Grype installation directory: $(which grype)"
   139    echo "Trivy installation directory: $(which trivy)"
   140  }
   141  
   142  while getopts 'hb:a:o:n:r:p:x:' opt; do
   143    case $opt in
   144    a)
   145      ALLOW_LIST=$OPTARG
   146      ;;
   147    b)
   148      BOM_FILE=$OPTARG
   149      ;;
   150    n)
   151      OCIR_NAMESPACE=$OPTARG
   152      ;;
   153    o)
   154      OUTPUT_DIR=$OPTARG
   155      ;;
   156    r)
   157      REGISTRY=$OPTARG
   158      ;;
   159    p)
   160      REPO_PATH=$OPTARG
   161      ;;
   162    x)
   163      COMBINED_PATH=$OPTARG
   164      REPO_PATH=$(echo $COMBINED_PATH | cut -d "/" -f2-)
   165      ;;
   166    h | ?)
   167      usage
   168      ;;
   169    esac
   170  done
   171  
   172  validate_inputs
   173  verify_prerequisites
   174  scan_images_in_bom || exit 1