github.com/verrazzano/verrazzano@v1.7.0/release/scripts/scan_release_binaries.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  # Perform malware scan on release binaries
     7  
     8  set -e
     9  
    10  SCRIPT_DIR=$(cd $(dirname "$0"); pwd -P)
    11  
    12  usage() {
    13      cat <<EOM
    14    Performs malware scan on release binaries.
    15  
    16    Usage:
    17      $(basename $0) <directory containing the release artifacts> <directory to download the scanner> <directory to place the scan report>
    18          <type of distribution - Lite/Full> <flag to indicate whether to skip installing scanner>
    19  
    20    Example:
    21      $(basename $0) release_bundle_dir scanner_home scan_report_dir
    22  
    23    The script expects the following environment variables -
    24      RELEASE_VERSION - release version (major.minor.patch format, e.g. 1.0.1)
    25      if installing command line scanner using local file:
    26           SCANNER_TARGZ_FILE - command line scanner (in a local tar.gz)
    27      if installing command line scanner using URL location:
    28           SCANNER_ARCHIVE_LOCATION - command line scanner
    29           SCANNER_ARCHIVE_FILE - scanner archive
    30      VIRUS_DEFINITION_LOCATION - virus definition location
    31      NO_PROXY_SUFFIX - suffix for no_proxy environment variable
    32  EOM
    33      exit 0
    34  }
    35  
    36  [ -z "$SCANNER_TARGZ_FILE" ] && ( [ -z "$SCANNER_ARCHIVE_LOCATION" ] || [ -z "$SCANNER_ARCHIVE_FILE" ] ) && { usage; }
    37  [ -z "$NO_PROXY_SUFFIX" ] || [ -z "$VIRUS_DEFINITION_LOCATION" ] || [ -z "$RELEASE_VERSION" ] || [ "$1" == "-h" ] && { usage; }
    38  
    39  . $SCRIPT_DIR/common.sh
    40  
    41  if [ -z "$1" ]; then
    42    echo "Directory to download the bundle or directory containing the release bundle extracted is required"
    43    exit 1
    44  fi
    45  RELEASE_BUNDLE_DOWNLOAD_DIR="$1"
    46  
    47  if [ -z "$2" ]; then
    48    echo "Directory to place the scanner is required"
    49    exit 1
    50  fi
    51  SCANNER_HOME="$2"
    52  
    53  if [ -z "$3" ]; then
    54    echo "Directory to place the scan report is required"
    55    exit 1
    56  fi
    57  SCAN_REPORT_DIR="$3"
    58  
    59  if [ -z "$4" ]; then
    60    echo "Verrazzano distribution type to scan is required"
    61    exit 1
    62  fi
    63  BUNDLE_TO_SCAN="$4"
    64  
    65  SKIP_INSTALL_SCANNER=${5:-"false"}
    66  
    67  DIR_TO_SCAN="$RELEASE_BUNDLE_DOWNLOAD_DIR"
    68  if [ "$BUNDLE_TO_SCAN" == "Full" ];then
    69    # There will be a top level verrazzano-<major>.<minor>.<patch> directory inside the full bundle
    70    DIR_TO_SCAN="$RELEASE_BUNDLE_DOWNLOAD_DIR/verrazzano-${RELEASE_VERSION}"
    71  fi
    72  SCAN_REPORT="$SCAN_REPORT_DIR/scan_report.out"
    73  
    74  export NO_PROXY="${ORACLE_NO_PROXY},${NO_PROXY_SUFFIX}"
    75  export HTTP_PROXY="${ORACLE_PROXY}"
    76  export HTTPS_PROXY="${ORACLE_PROXY}"
    77  
    78  function install_local_scanner() {
    79    echo "Installing scanner from local file: $SCANNER_TARGZ_FILE"
    80    mkdir -p $SCANNER_HOME
    81    cd $SCANNER_HOME
    82    tar --overwrite -xvf $SCANNER_TARGZ_FILE
    83  }
    84  
    85  function install_remote_scanner() {
    86    echo "Downloading and installing scanner from: $SCANNER_ARCHIVE_LOCATION/$SCANNER_ARCHIVE_FILE"
    87    mkdir -p $SCANNER_HOME
    88    cd $SCANNER_HOME
    89    curl -O $SCANNER_ARCHIVE_LOCATION/$SCANNER_ARCHIVE_FILE
    90    tar --overwrite -xvf $SCANNER_ARCHIVE_FILE
    91  }
    92  
    93  function update_virus_definition() {
    94    VIRUS_DEF_FILE=$(curl -s $VIRUS_DEFINITION_LOCATION | grep -oP 'avvdat-.*?zip' | sort -nr | head -1)
    95    cd $SCANNER_HOME
    96    curl -O $VIRUS_DEFINITION_LOCATION/$VIRUS_DEF_FILE
    97    unzip -o $VIRUS_DEF_FILE
    98  }
    99  
   100  function scan_release_binaries() {
   101    mkdir -p $SCAN_REPORT_DIR
   102    if [ -e "$SCAN_REPORT" ]; then
   103      rm -f $SCAN_REPORT
   104    fi
   105  
   106    cd $DIR_TO_SCAN
   107    ls
   108    local count_files=$(find . -maxdepth 5 -type f  | LC_ALL=C grep -c /)
   109  
   110    cd $SCANNER_HOME
   111    # The scan takes more than 50 minutes, the option --SUMMARY prints each and every file from all the layers, which is removed.
   112    # Also --REPORT option prints the output of the scan in the console, which is removed and redirected to a file
   113    echo "Starting the scan of $DIR_TO_SCAN, it might take a longer duration."
   114    echo "The output of the scan is being written to $SCAN_REPORT ..."
   115    ./uvscan $DIR_TO_SCAN --RPTALL --RECURSIVE --CLEAN --UNZIP --VERBOSE --SUB --SUMMARY --PROGRAM --RPTOBJECTS --AFC=512 >> $SCAN_REPORT 2>&1
   116  
   117    # Extract only the last 25 lines from the scan report and create a file, which will be used for the validation
   118    local scan_summary="${SCAN_REPORT_DIR}/scan_summary.out"
   119    if [ -e "${scan_summary}" ]; then
   120      rm -f $scan_summary
   121    fi
   122      tail -25 ${SCAN_REPORT} > ${scan_summary}
   123  
   124    # Get the files not scanned count from the summary
   125    local files_not_scanned=$(grep 'Not Scanned:...................     ' $scan_summary | sed 's/\tNot Scanned:...................     //g')
   126  
   127    echo "File count: $count_files"
   128    echo "Files not scanned: $files_not_scanned"
   129  
   130    # Default the number of clean files expected to all of them (lite distribution all must be clean, full dist we allow for some skips and
   131    # in that case will adjust this below if required)
   132    local clean_files="$count_files"
   133  
   134    # Workaround to address the issue where scanner fails to open a file from ghcr.io_verrazzano_fluentd-kubernetes-daemonset image
   135    if [ "$BUNDLE_TO_SCAN" == "Full" ];then
   136      # The scanner has had issues in the past where it fails to open a file (seen in ghcr.io_verrazzano_fluentd-kubernetes-daemonset image)
   137      # We have a workaround that allows up to 1 file to not be scanned and still pass the checking here.
   138      if [[ $files_not_scanned -gt 1 ]]; then
   139        echo "There were $files_not_scanned files that were not scanned, that exceeds the threshold for the full distribution and the scan is being treated as failed"
   140        return 1
   141      fi
   142      # Update the expectation for the number of clean files to include any not scanned
   143      clean_files="$(expr $count_files - $files_not_scanned)"
   144    else
   145      # For the lite distribution
   146      if [[ $files_not_scanned -gt 0 ]]; then
   147        echo "There were $files_not_scanned files that were not scanned, none can be skipped for the lite distribution and the scan is being treated as failed"
   148        return 1
   149      fi
   150    fi
   151  
   152    # The following set of lines from the summary in the scan report is used here for validation.
   153    declare -a expectedLines=("Total files:...................     $count_files"
   154                              "Clean:.........................     $clean_files"
   155                              "Not Scanned:...................     $files_not_scanned"
   156                              "Possibly Infected:.............     0"
   157                              "Objects Possibly Infected:.....     0"
   158                              "Cleaned:.......................     0"
   159                              "Deleted:.......................     0")
   160  
   161    array_count=${#expectedLines[@]}
   162    echo "Count of expected lines: ${array_count}"
   163    result_count=0
   164  
   165    # Read the file scan_summary.out line by line and increment the counter when the line matches one of the expected lines defined above.
   166    while IFS= read -r line
   167    do
   168      for i in "${expectedLines[@]}"
   169      do
   170        case $line in
   171          *${i}*)
   172            result_count=$(($result_count+1))
   173            ;;
   174          *)
   175        esac
   176      done
   177    done < "$scan_summary"
   178    echo "Count of expected lines in the scan summary: ${result_count}"
   179    if [ "$result_count" == "$array_count" ];then
   180      echo "Found all the expected lines in the summary of the scan report."
   181      return 0
   182    else
   183      echo "One or more expected lines are not found in the summary of the scan report, please check the complete report $SCAN_REPORT"
   184      return 1
   185    fi
   186  }
   187  
   188  # Skip installation of scanner if SKIP_INSTALL_SCANNER is true
   189  if [ "true" == "${SKIP_INSTALL_SCANNER}" ];then
   190    echo "Skip installing scanner ..."
   191  else
   192    if [ -f "${SCANNER_TARGZ_FILE}" ]; then
   193      install_local_scanner || exit 1
   194    else
   195      install_remote_scanner || exit 1
   196    fi
   197    update_virus_definition || exit 1
   198  fi
   199  
   200  scan_release_binaries || exit 1