Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 70 additions & 22 deletions build-image/jfrog-scanning/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,19 @@ runs:
JF_USER: ${{ inputs.jfrog-username }}
JF_PASSWORD: ${{ inputs.jfrog-password }}

- name: Prepare JFrog report paths
id: report_paths
shell: bash
run: |
set -euo pipefail

TIMESTAMP="$(date +"%Y-%m-%d_%H-%M-%S")"
FULL_REPORT="$GITHUB_WORKSPACE/jfrog-scan-full.json"
TABLE_REPORT="$GITHUB_WORKSPACE/jfrog-scan-table-${TIMESTAMP}.txt"

echo "full_report_file=$FULL_REPORT" >> "$GITHUB_OUTPUT"
echo "table_report_file=$TABLE_REPORT" >> "$GITHUB_OUTPUT"

- name: Check for existing dist zip
id: check_dist
shell: bash
Expand Down Expand Up @@ -126,31 +139,55 @@ runs:
${{ inputs.build-server-arguments }}

- name: Run the JFrog scan
if: steps.check_dist.outputs.dist_exists == 'true' || steps.check_task.outputs.appDistZip == 'true'
id: scan
if: steps.check_dist.outputs.dist_exists == 'true' || steps.check_task.outputs.appDistZip == 'true'
shell: bash
working-directory: ${{ inputs.working-directory }}/${{ inputs.server-path }}
run: |
set -euo pipefail
set -uo pipefail

TIMESTAMP="$(date +"%Y-%m-%d_%H-%M-%S")"
FULL_REPORT="$GITHUB_WORKSPACE/jfrog-scan-full.json"
TABLE_REPORT="$GITHUB_WORKSPACE/jfrog-scan-table-${TIMESTAMP}.txt"
FULL_REPORT="${{ steps.report_paths.outputs.full_report_file }}"
TABLE_REPORT="${{ steps.report_paths.outputs.table_report_file }}"

shopt -s nullglob
zips=(*/build/dist/*.zip)

if [ "${#zips[@]}" -eq 0 ]; then
echo "::error::No distribution ZIP files were found for JFrog scanning."
exit 1

printf '%s\n' \
'{"error":"No distribution ZIP files were found","vulnerabilities":[]}' \
> "$FULL_REPORT"

{
printf 'CVE\tSeverity\tComponent\tFixed Versions\n'
printf '%s\n' '--- --- --- ---'
} > "$TABLE_REPORT"

echo "scan_exit_code=1" >> "$GITHUB_OUTPUT"
exit 0
fi

set +e

jf s \
--licenses \
--vuln \
--format=json \
"${zips[@]}" > "$FULL_REPORT"

JFROG_EXIT_CODE=$?

set -e

echo "scan_exit_code=$JFROG_EXIT_CODE" >> "$GITHUB_OUTPUT"

if [ ! -s "$FULL_REPORT" ]; then
printf '%s\n' \
"{\"error\":\"JFrog scan failed\",\"exit_code\":$JFROG_EXIT_CODE,\"vulnerabilities\":[]}" \
> "$FULL_REPORT"
fi

if ! jq -r '
def roots:
if type == "array" then
Expand Down Expand Up @@ -181,44 +218,55 @@ runs:
{
printf 'CVE\tSeverity\tComponent\tFixed Versions\n'
printf '%s\n' '--- --- --- ---'
printf 'N/A\tERROR\tJFrog scan failed\tN/A\n'
} > "$TABLE_REPORT"
fi

echo "full_report_file=$FULL_REPORT" >> "$GITHUB_OUTPUT"
echo "table_report_file=$TABLE_REPORT" >> "$GITHUB_OUTPUT"
exit 0

- name: Create markdown summary
if: steps.scan.outcome == 'success'
if: always() && steps.scan.outputs.scan_exit_code == '0'
uses: genesislcap/appdev-workflows/build-image/json-to-md@develop
with:
json_path: ${{ steps.scan.outputs.full_report_file }}
json_path: ${{ steps.report_paths.outputs.full_report_file }}

- name: Upload JFrog full scan report
if: always() && steps.scan.outputs.full_report_file != ''
if: always()
uses: actions/upload-artifact@v4
with:
name: xray-full-report--${{ inputs.app-name }}-${{ github.run_number }}
path: ${{ steps.scan.outputs.full_report_file }}
if-no-files-found: error
name: xray-full-report-${{ inputs.app-name }}-${{ github.run_number }}
path: ${{ steps.report_paths.outputs.full_report_file }}
if-no-files-found: warn

- name: Upload JFrog scan table report
if: always() && steps.scan.outputs.table_report_file != ''
if: always()
uses: actions/upload-artifact@v4
with:
name: xray-dependencies-scan--${{ inputs.app-name }}-${{ github.run_number }}
path: ${{ steps.scan.outputs.table_report_file }}
if-no-files-found: error
name: xray-dependencies-scan-${{ inputs.app-name }}-${{ github.run_number }}
path: ${{ steps.report_paths.outputs.table_report_file }}
if-no-files-found: warn

- name: Evaluate JFrog vulnerability threshold
if: steps.scan.outcome == 'success'
if: always()
shell: bash
run: |
set -euo pipefail

FULL_REPORT="${{ steps.scan.outputs.full_report_file }}"
FULL_REPORT="${{ steps.report_paths.outputs.full_report_file }}"
SCAN_EXIT_CODE="${{ steps.scan.outputs.scan_exit_code }}"
FAIL_BUILD="${{ inputs.fail-build }}"

if [ -z "$FULL_REPORT" ] || [ ! -f "$FULL_REPORT" ]; then
if [ -z "$SCAN_EXIT_CODE" ]; then
echo "::error::JFrog scan did not execute."
exit 1
fi

if [ "$SCAN_EXIT_CODE" -ne 0 ]; then
echo "::error::JFrog scan failed with exit code $SCAN_EXIT_CODE."
exit 1
fi

if [ ! -s "$FULL_REPORT" ]; then
echo "::error::JFrog scan report was not generated."
exit 1
fi
Expand Down Expand Up @@ -256,7 +304,7 @@ runs:
exit 1
fi

echo "::warning::JFrog Xray found $HIGH_CRITICAL_FINDINGS High or Critical vulnerabilities. Blocking is disabled for this application."
echo "::warning::JFrog Xray found $HIGH_CRITICAL_FINDINGS High or Critical vulnerabilities. Blocking is disabled."
exit 0
fi

Expand Down
112 changes: 81 additions & 31 deletions build-image/trivy-scanning/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ inputs:
outputs:
json_path:
description: Path to the Trivy JSON report
value: ${{ steps.scan.outputs.json_path }}
value: ${{ steps.meta.outputs.json_file }}

html_path:
description: Path to the Trivy HTML report
value: ${{ steps.scan.outputs.html_path }}
value: ${{ steps.meta.outputs.html_file }}

markdown_path:
description: Path to the Trivy Markdown report
value: ${{ steps.scan.outputs.markdown_path }}
value: ${{ steps.meta.outputs.md_file }}

threshold_findings:
description: Number of vulnerabilities matching the configured threshold
Expand All @@ -65,7 +65,6 @@ runs:
sh -s -- -b "$TRIVY_BIN_DIR"

echo "$TRIVY_BIN_DIR" >> "$GITHUB_PATH"

"$TRIVY_BIN_DIR/trivy" --version

- name: Prepare report paths
Expand Down Expand Up @@ -104,12 +103,11 @@ runs:
id: scan
shell: bash
run: |
set -euo pipefail
set -uo pipefail

TRIVY_BIN="$RUNNER_TEMP/trivy-bin/trivy"
JSON_FILE="${{ steps.meta.outputs.json_file }}"
HTML_FILE="${{ steps.meta.outputs.html_file }}"
MD_FILE="${{ steps.meta.outputs.md_file }}"
IMAGE_REF="${{ inputs.image-ref }}"

trivy_args=(
Expand All @@ -125,29 +123,71 @@ runs:

trivy_args+=("$IMAGE_REF")

set +e
"$TRIVY_BIN" "${trivy_args[@]}"
TRIVY_EXIT_CODE=$?
set -e

echo "scan_exit_code=$TRIVY_EXIT_CODE" >> "$GITHUB_OUTPUT"

if [ ! -s "$JSON_FILE" ]; then
cat > "$JSON_FILE" <<EOF
{
"SchemaVersion": 2,
"ArtifactName": "$IMAGE_REF",
"ArtifactType": "container_image",
"Results": [],
"ScanError": "Trivy scan failed with exit code $TRIVY_EXIT_CODE"
}
EOF
fi

set +e
node "${{ github.action_path }}/render-trivy-html.mjs" \
"$JSON_FILE" \
"$HTML_FILE"
HTML_EXIT_CODE=$?
set -e

if [ "$HTML_EXIT_CODE" -ne 0 ] || [ ! -s "$HTML_FILE" ]; then
cat > "$HTML_FILE" <<EOF
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Trivy scan report</title>
</head>
<body>
<h1>Trivy scan report</h1>
<p>Image: $IMAGE_REF</p>
<p>HTML report generation failed.</p>
<p>See the JSON artifact for complete scan output.</p>
</body>
</html>
EOF
fi

echo "json_path=$JSON_FILE" >> "$GITHUB_OUTPUT"
echo "html_path=$HTML_FILE" >> "$GITHUB_OUTPUT"
echo "markdown_path=$MD_FILE" >> "$GITHUB_OUTPUT"
exit 0

- name: Create markdown summary
if: always()
shell: bash
run: |
set -euo pipefail

JSON_FILE="${{ steps.scan.outputs.json_path }}"
MD_FILE="${{ steps.scan.outputs.markdown_path }}"
JSON_FILE="${{ steps.meta.outputs.json_file }}"
MD_FILE="${{ steps.meta.outputs.md_file }}"
IMAGE_REF="${{ inputs.image-ref }}"
SEVERITY_THRESHOLD="${{ inputs.severity-threshold }}"

if [ ! -f "$JSON_FILE" ]; then
echo "::error::Trivy JSON report not found: $JSON_FILE"
exit 1
if [ ! -s "$JSON_FILE" ]; then
{
printf '# Trivy Scan Summary\n\n'
printf 'Image: `%s`\n\n' "$IMAGE_REF"
printf 'Trivy did not generate a JSON report.\n'
} > "$MD_FILE"

exit 0
fi

jq_count() {
Expand Down Expand Up @@ -195,47 +235,57 @@ runs:

printf '%s\n' '---' >> "$MD_FILE"

echo "Wrote markdown report to $MD_FILE"

if [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then
cat "$MD_FILE" >> "$GITHUB_STEP_SUMMARY"
fi

- name: Upload Trivy JSON report
if: always() && steps.scan.outputs.json_path != ''
if: always()
uses: actions/upload-artifact@v4
with:
name: trivy-scan-json--${{ steps.meta.outputs.app_name }}-${{ github.run_number }}
path: ${{ steps.scan.outputs.json_path }}
if-no-files-found: error
name: trivy-scan-json-${{ steps.meta.outputs.app_name }}-${{ github.run_number }}
path: ${{ steps.meta.outputs.json_file }}
if-no-files-found: warn

- name: Upload Trivy HTML report
if: always() && steps.scan.outputs.html_path != ''
if: always()
uses: actions/upload-artifact@v4
with:
name: trivy-scan-html--${{ steps.meta.outputs.app_name }}-${{ github.run_number }}
path: ${{ steps.scan.outputs.html_path }}
if-no-files-found: error
name: trivy-scan-html-${{ steps.meta.outputs.app_name }}-${{ github.run_number }}
path: ${{ steps.meta.outputs.html_file }}
if-no-files-found: warn

- name: Upload Trivy markdown report
if: always() && steps.scan.outputs.markdown_path != ''
if: always()
uses: actions/upload-artifact@v4
with:
name: trivy-scan-md--${{ steps.meta.outputs.app_name }}-${{ github.run_number }}
path: ${{ steps.scan.outputs.markdown_path }}
if-no-files-found: error
name: trivy-scan-md-${{ steps.meta.outputs.app_name }}-${{ github.run_number }}
path: ${{ steps.meta.outputs.md_file }}
if-no-files-found: warn

- name: Evaluate Trivy vulnerability threshold
id: gate
if: always()
shell: bash
run: |
set -euo pipefail

JSON_FILE="${{ steps.scan.outputs.json_path }}"
JSON_FILE="${{ steps.meta.outputs.json_file }}"
SCAN_EXIT_CODE="${{ steps.scan.outputs.scan_exit_code }}"
SEVERITY_THRESHOLD="${{ inputs.severity-threshold }}"
FAIL_BUILD="${{ inputs.fail-build }}"

if [ -z "$JSON_FILE" ] || [ ! -f "$JSON_FILE" ]; then
if [ -z "$SCAN_EXIT_CODE" ]; then
echo "::error::Trivy scan did not execute."
exit 1
fi

if [ "$SCAN_EXIT_CODE" -ne 0 ]; then
echo "::error::Trivy scan failed with exit code $SCAN_EXIT_CODE."
exit 1
fi

if [ ! -s "$JSON_FILE" ]; then
echo "::error::Trivy JSON report was not generated."
exit 1
fi
Expand Down Expand Up @@ -277,7 +327,7 @@ runs:
exit 1
fi

echo "::warning::Trivy found $THRESHOLD_FINDINGS vulnerabilities matching threshold '$SEVERITY_THRESHOLD'. Blocking is disabled for this application."
echo "::warning::Trivy found $THRESHOLD_FINDINGS vulnerabilities matching threshold '$SEVERITY_THRESHOLD'. Blocking is disabled."
exit 0
fi

Expand Down
Loading