#!/bin/bash # Function to print usage information usage() { echo "Usage: $0 " echo "Example: $0 tabledevil/sep 230101 230916 username dckr_pat_8FEgaA5ovvL1V-UEWfV5T3jVABC" exit 1 } # Check if the correct number of arguments is provided if [ "$#" -ne 5 ]; then usage fi # Assign arguments to variables image="$1" start_tag="$2" end_tag="$3" DOCKER_USERNAME="$4" DOCKER_PAT="$5" REPOSITORY=$(echo "$image" | cut -d'/' -f2) PATTERN="Threat Found!" # Validate that start_tag and end_tag are in the correct format if ! [[ "$start_tag" =~ ^[0-9]{6}$ ]] || ! [[ "$end_tag" =~ ^[0-9]{6}$ ]]; then echo "Error: Tags must be in YYMMDD format." usage fi # Function to get Docker Hub token using PAT get_token() { TOKEN=$(curl -s -H "Content-Type: application/json" -X POST -d '{"username": "'${DOCKER_USERNAME}'", "password": "'${DOCKER_PAT}'"}' https://hub.docker.com/v2/users/login/ | jq -r .token) if [ "$TOKEN" == "null" ]; then echo "Failed to get token. Please check your credentials." exit 1 fi } # Function to get tags for a repository get_tags() { PAGE=1 PAGE_SIZE=100 TAGS=() while true; do RESPONSE=$(curl -s -H "Authorization: JWT ${TOKEN}" "https://hub.docker.com/v2/repositories/${DOCKER_USERNAME}/${REPOSITORY}/tags/?page_size=${PAGE_SIZE}&page=${PAGE}") TAGS_PAGE=$(echo $RESPONSE | jq -r '.results[].name') TAGS+=($TAGS_PAGE) NEXT=$(echo $RESPONSE | jq -r '.next') if [ "$NEXT" == "null" ]; then break fi PAGE=$((PAGE + 1)) done echo "${TAGS[@]}" } # Function to filter tags based on date range filter_tags_by_date() { local tags=("$@") local filtered=() local start="$start_tag" local end="$end_tag" for tag in "${tags[@]}"; do if [[ "$tag" =~ ^[0-9]{6}$ ]]; then if [[ "$tag" -ge "$start" && "$tag" -le "$end" ]]; then filtered+=("$tag") fi fi done echo "${filtered[@]}" } # Function to check for malware in the given Docker tag check_malware() { local tag=$1 echo -n "Checking $image:$tag " result=$(docker run -it --rm -v "$(pwd):/data:ro" --network=none "$image:$tag" scan) if echo "$result" | grep -q "$PATTERN"; then echo "Pattern found" return 0 else echo "Pattern NOT found" return 1 fi } # Function to run the binary search binary_search() { local tags=("$@") local low=0 local high=$(( ${#tags[@]} - 1 )) local mid # Determine the initial states for low and high check_malware "${tags[$low]}" local low_result=$? check_malware "${tags[$high]}" local high_result=$? # If the results for low and high are the same, there is no switch point in the range if [ $low_result -eq $high_result ]; then echo "No change in detection within the tag range." return fi # Binary search to find the exact switching point while [ $((low + 1)) -lt $high ]; do mid=$(((low + high) / 2)) check_malware "${tags[$mid]}" local mid_result=$? if [ $mid_result -eq $low_result ]; then low=$mid else high=$mid fi done # Output the tag of the first image that finds the malware switch echo "The detection changes between tags: ${tags[$low]} - ${tags[$high]}" } # Main script execution get_token # Retrieve all tags from Docker Hub echo "Retrieving all tags for $image from Docker Hub..." all_tags=($(get_tags)) # Filter tags to include only those within the date range echo "Filtering tags from $start_tag to $end_tag..." filtered_tags=($(filter_tags_by_date "${all_tags[@]}")) # Run the binary search on the filtered tags echo "Running binary search on the filtered tags..." binary_search "${filtered_tags[@]}"