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