2424 required : false
2525 type : boolean
2626 default : false
27+ prebuilt-image-artifact :
28+ description : ' Name of uploaded artifact containing a Docker image tar (skip Docker build if provided)'
29+ required : false
30+ type : string
31+ default : ' '
32+ prebuilt-image-names :
33+ description : ' Space-separated list of Docker image:tag names inside the prebuilt artifact tar'
34+ required : false
35+ type : string
36+ default : ' '
2737
2838jobs :
2939 wiz-scan :
5565 # if: ${{ !inputs.wiz-image-skip-aws }}
5666 # uses: aws-actions/amazon-ecr-login@v2
5767
68+ - name : Download prebuilt Docker image
69+ if : ${{ inputs.prebuilt-image-artifact != '' }}
70+ uses : actions/download-artifact@v4
71+ with :
72+ name : ${{ inputs.prebuilt-image-artifact }}
73+ path : /tmp
74+
75+ - name : Load prebuilt Docker image
76+ id : load-image
77+ if : ${{ inputs.prebuilt-image-artifact != '' }}
78+ run : |
79+ echo "Loading prebuilt images from artifact..."
80+ docker load -i /tmp/docker-image.tar
81+ echo "IMAGES=${{ inputs.prebuilt-image-names }}" >> "$GITHUB_OUTPUT"
82+ echo "Loaded images: ${{ inputs.prebuilt-image-names }}"
83+ docker images
84+
5885 - name : Build Docker image
5986 id : build-image
87+ if : ${{ inputs.prebuilt-image-artifact == '' }}
6088 env :
6189 GITHUB_TOKEN : ${{ secrets.GH_TOKEN }}
6290 run : |
@@ -74,60 +102,55 @@ jobs:
74102 chmod +x build-docker.sh
75103 GITHUB_TOKEN="${{ secrets.GH_TOKEN }}" ./build-docker.sh
76104
77- IMAGE =$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -E "^${REPO_NAME}" | grep -v "^<none>" | head -1 )
105+ IMAGES =$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -E "^${REPO_NAME}" | grep -v "^<none>")
78106
79- if [ -z "$IMAGE " ]; then
80- echo "⚠️ No image found with prefix ${REPO_NAME} after build-docker.sh"
107+ if [ -z "$IMAGES " ]; then
108+ echo "⚠️ No images found with prefix ${REPO_NAME} after build-docker.sh"
81109 echo "Checking for any recently built images..."
82- IMAGE =$(docker images --format "{{.CreatedAt}}\t{{.Repository}}:{{.Tag}}" | sort -r | head -1 | cut -f2)
110+ IMAGES =$(docker images --format "{{.CreatedAt}}\t{{.Repository}}:{{.Tag}}" | sort -r | head -5 | cut -f2 | grep -v "^<none>" )
83111 fi
84112 # Strategy 2: Check for Makefile with compose-build target
85113 elif [ -f "Makefile" ] && grep -q "^compose-build:" Makefile; then
86114 echo "Using Makefile compose-build target with GITHUB_TOKEN"
87115 export GITHUB_TOKEN="${{ secrets.GH_TOKEN }}"
88-
89- # Record image IDs before build to detect newly built images
90- BEFORE_IDS=$(docker images -q --no-trunc | sort)
91-
92116 make compose-build
93117
94- echo "Detecting built image..."
95- # Find newly created images by comparing before/after image IDs
96- AFTER_IDS=$(docker images -q --no-trunc | sort)
97- NEW_IDS=$(comm -13 <(echo "$BEFORE_IDS") <(echo "$AFTER_IDS"))
98-
99- if [ -n "$NEW_IDS" ]; then
100- for id in $NEW_IDS; do
101- # Use docker inspect instead of --filter (compatible with all Docker versions)
102- IMAGE=$(docker inspect --format '{{index .RepoTags 0}}' "$id" 2>/dev/null || true)
103- [ -n "$IMAGE" ] && [ "$IMAGE" != "<none>:<none>" ] && break
104- IMAGE=""
105- done
106- fi
118+ echo "Detecting built images..."
119+ docker compose images
107120
108- if [ -z "$IMAGE" ]; then
109- echo "No new image detected by ID comparison, falling back to repo name match"
110- IMAGE=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -E "^${REPO_NAME}" | grep -v "<none>" | head -1)
111- fi
121+ IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep "^${REPO_NAME}" | grep -v "^<none>")
112122
113- if [ -z "$IMAGE " ]; then
114- echo "⚠️ No image found matching ${REPO_NAME}, using most recent non-runner image "
115- IMAGE =$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -v "^<none>" | grep -v "^ghcr.io/github/" | grep -v "^ghcr.io/dependabot/" | head -1 )
123+ if [ -z "$IMAGES " ]; then
124+ echo "No images found with prefix ${REPO_NAME}, scanning all recent images "
125+ IMAGES =$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -v "^<none>" | head -5 )
116126 fi
117127 # Strategy 3: Fallback to standard docker build
118128 else
119129 echo "Using standard docker build with GITHUB_TOKEN build arg"
120130 docker build --build-arg GITHUB_TOKEN="${{ secrets.GH_TOKEN }}" -t "${REPO_NAME}:latest" .
121- IMAGE ="${REPO_NAME}:latest"
131+ IMAGES ="${REPO_NAME}:latest"
122132 fi
123133
124- if [ -z "$IMAGE " ]; then
125- echo "❌ No Docker image found after build"
134+ if [ -z "$IMAGES " ]; then
135+ echo "❌ No Docker images found after build"
126136 exit 1
127137 fi
128138
129- echo "Image to scan: $IMAGE"
130- echo "IMAGE=$IMAGE" >> "$GITHUB_OUTPUT"
139+ echo "Found images to scan:"
140+ echo "$IMAGES"
141+ echo "IMAGES=$(echo $IMAGES | tr '\n' ' ')" >> "$GITHUB_OUTPUT"
142+
143+ - name : Set scan target images
144+ id : scan-target
145+ run : |
146+ # Use prebuilt images if available, otherwise use freshly built images
147+ IMAGES="${{ steps.load-image.outputs.IMAGES || steps.build-image.outputs.IMAGES }}"
148+ if [ -z "$IMAGES" ]; then
149+ echo "❌ No Docker images available for scanning"
150+ exit 1
151+ fi
152+ echo "Scan targets: $IMAGES"
153+ echo "IMAGES=$IMAGES" >> "$GITHUB_OUTPUT"
131154
132155 - name : Fetch Wiz credentials from AKeyless
133156 id : fetch-secrets
@@ -150,50 +173,55 @@ jobs:
150173 run : |
151174 set -o pipefail
152175
153- SCAN_TYPE='container-image '
154- SCAN_TARGET='${{ steps.build-image.outputs.IMAGE }}'
176+ IMAGES='${{ steps.scan-target.outputs.IMAGES }} '
177+ SCAN_RESULT="passed"
155178
156- args=("$SCAN_TYPE")
157- if [ -n "$SCAN_TARGET" ]; then
158- args+=("$SCAN_TARGET")
159- fi
179+ # Initialize combined output files
180+ > /tmp/wiz-scan.json
181+ > /tmp/wiz-scan-results.txt
160182
161- # Always output JSON for severity analysis
162- args+=("--json-output-file" "/tmp/wiz-scan.json")
183+ for IMAGE_NAME in $IMAGES; do
184+ echo ""
185+ echo "============================================"
186+ echo "Scanning Docker image: $IMAGE_NAME"
187+ echo "============================================"
188+
189+ JSON_FILE="/tmp/wiz-scan-$(echo $IMAGE_NAME | tr '/:' '__').json"
190+
191+ ./wizcli scan container-image "$IMAGE_NAME" --json-output-file "$JSON_FILE" 2>&1 | tee -a /tmp/wiz-scan-results.txt
192+ EXIT_CODE=${PIPESTATUS[0]}
163193
164- ./wizcli scan "${args[@]}" 2>&1 | tee /tmp/wiz-scan-results.txt
165- EXIT_CODE=${PIPESTATUS[0]}
194+ # Append JSON result to combined file
195+ cat "$JSON_FILE" >> /tmp/wiz-scan.json 2>/dev/null || true
196+
197+ if [ $EXIT_CODE -ne 0 ]; then
198+ SCAN_RESULT="failed"
199+ fi
200+ done
166201
167202 echo ""
168203 echo "============================================"
169- echo "Wiz CLI Scan Output"
170- echo "============================================"
171- cat /tmp/wiz-scan-results.txt
204+ echo "Wiz CLI Scan Complete - Result: $SCAN_RESULT"
172205 echo "============================================"
173- echo ""
174206
175- if [ $EXIT_CODE -eq 0 ]; then
176- echo "scan-result=passed" >> $GITHUB_OUTPUT
177- else
178- echo "scan-result=failed" >> $GITHUB_OUTPUT
179- fi
180- exit $EXIT_CODE
207+ echo "scan-result=$SCAN_RESULT" >> $GITHUB_OUTPUT
208+ [ "$SCAN_RESULT" = "failed" ] && exit 1 || exit 0
181209
182210 - name : Check Wiz results for severity violations
183211 id : severity-check
184212 if : always()
185213 run : |
186- JSON_FILE="/tmp/wiz-scan.json"
187-
188- if [ ! -f "$JSON_FILE" ]; then
189- echo "⚠️ Wiz JSON output not found, skipping severity check"
190- echo "severity-result=skipped" >> $GITHUB_OUTPUT
191- exit 0
192- fi
193-
194- # Count vulnerabilities by severity from Wiz JSON analytics summary
195- CRITICAL_COUNT =$(jq '.result.analytics.vulnerabilities.criticalCount // 0' "$JSON_FILE" 2>/dev/null || echo "0" )
196- HIGH_COUNT=$(jq '.result.analytics.vulnerabilities.highCount // 0' "$JSON_FILE" 2>/dev/null || echo "0")
214+ # Aggregate vulnerability counts across all per-image JSON files
215+ CRITICAL_COUNT=0
216+ HIGH_COUNT=0
217+
218+ for JSON_FILE in /tmp/wiz-scan-*.json; do
219+ [ -f "$JSON_FILE" ] || continue
220+ C=$(jq '.result.analytics.vulnerabilities.criticalCount // 0' "$JSON_FILE" 2>/dev/null || echo "0")
221+ H=$(jq '.result.analytics.vulnerabilities.highCount // 0' "$JSON_FILE" 2>/dev/null || echo "0")
222+ CRITICAL_COUNT=$((CRITICAL_COUNT + C))
223+ HIGH_COUNT =$((HIGH_COUNT + H) )
224+ done
197225
198226 echo ""
199227 echo "============================================"
@@ -243,7 +271,7 @@ jobs:
243271 echo "| Field | Value |"
244272 echo "|-------|-------|"
245273 echo "| **Scan type** | \`container-image\` |"
246- echo "| **Target** | \`${{ steps.build-image .outputs.IMAGE }}\` |"
274+ echo "| **Target(s) ** | \`${{ steps.scan-target .outputs.IMAGES }}\` |"
247275 echo "| **Policy result** | **${SCAN_RESULT}** |"
248276 echo "| **Severity result** | **${SEVERITY_RESULT}** |"
249277 echo "| **Overall** | **${OVERALL}** |"
@@ -267,5 +295,5 @@ jobs:
267295 with :
268296 name : wiz-scan-${{ github.event.repository.name }}
269297 path : |
270- /tmp/wiz-scan.json
298+ /tmp/wiz-scan* .json
271299 /tmp/wiz-scan-results.txt
0 commit comments