Skip to content

Trivy Repository Scan #8

Trivy Repository Scan

Trivy Repository Scan #8

Workflow file for this run

name: Trivy Repository Scan
on:
pull_request:
push:
branches:
- main
- master
schedule:
- cron: '20 6 * * 1'
workflow_dispatch:
permissions:
contents: read
security-events: write
concurrency:
group: trivy-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
trivy:
runs-on: ubuntu-latest
timeout-minutes: 45
outputs:
report_artifact: ${{ steps.report-outputs.outputs.report_artifact }}
json_report: ${{ steps.report-outputs.outputs.json_report }}
sarif_report: ${{ steps.report-outputs.outputs.sarif_report }}
text_report: ${{ steps.report-outputs.outputs.text_report }}
readable_report: ${{ steps.report-outputs.outputs.readable_report }}
java_json_report: ${{ steps.report-outputs.outputs.java_json_report }}
java_sarif_report: ${{ steps.report-outputs.outputs.java_sarif_report }}
java_text_report: ${{ steps.report-outputs.outputs.java_text_report }}
java_readable_report: ${{ steps.report-outputs.outputs.java_readable_report }}
java_sarif_html_report: ${{ steps.report-outputs.outputs.java_sarif_html_report }}
source_json_report: ${{ steps.report-outputs.outputs.source_json_report }}
source_sarif_report: ${{ steps.report-outputs.outputs.source_sarif_report }}
source_text_report: ${{ steps.report-outputs.outputs.source_text_report }}
source_sarif_html_report: ${{ steps.report-outputs.outputs.source_sarif_html_report }}
total_vulnerabilities: ${{ steps.report-outputs.outputs.total_vulnerabilities }}
critical_vulnerabilities: ${{ steps.report-outputs.outputs.critical_vulnerabilities }}
high_vulnerabilities: ${{ steps.report-outputs.outputs.high_vulnerabilities }}
medium_vulnerabilities: ${{ steps.report-outputs.outputs.medium_vulnerabilities }}
low_vulnerabilities: ${{ steps.report-outputs.outputs.low_vulnerabilities }}
unknown_vulnerabilities: ${{ steps.report-outputs.outputs.unknown_vulnerabilities }}
source_misconfigurations: ${{ steps.report-outputs.outputs.source_misconfigurations }}
source_secrets: ${{ steps.report-outputs.outputs.source_secrets }}
source_license_findings: ${{ steps.report-outputs.outputs.source_license_findings }}
env:
TRIVY_CACHE_DIR: .trivy-cache
TRIVY_ARTIFACT_SCAN_DIR: build/trivy-artifacts
TRIVY_REPORT_DIR: build/reports/trivy
TRIVY_SOURCE_SKIP_DIRS: archived,apps/netty-server,testing,build,**/build
TRIVY_SEVERITY: UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL
TRIVY_SKIP_VERSION_CHECK: true
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v5
with:
java-version: '25'
distribution: 'temurin'
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Build and collect Java artifacts for Trivy
run: |
cat > "${RUNNER_TEMP}/trivy-runtime-classpath.gradle" <<'EOF'
allprojects {
plugins.withType(JavaPlugin).configureEach {
if (project.path == ':apps:netty-server' || project.path.startsWith(':testing')) {
return
}
def runtimeClasspath = configurations.findByName('runtimeClasspath')
if (runtimeClasspath != null) {
tasks.register('copyTrivyRuntimeClasspath', Copy) {
from(runtimeClasspath)
into(rootProject.layout.buildDirectory.dir("trivy-artifacts/${project.path.replace(':', '/').replaceFirst('^/', '')}/runtimeClasspath"))
}
}
}
}
EOF
./gradlew clean jar copyTrivyRuntimeClasspath --no-daemon --console=plain --init-script "${RUNNER_TEMP}/trivy-runtime-classpath.gradle"
- name: Set Trivy cache key
id: trivy-cache-key
run: echo "date=$(date -u +%Y-%m-%d)" >> "${GITHUB_OUTPUT}"
- name: Restore Trivy cache
uses: actions/cache@v4
with:
path: ${{ env.TRIVY_CACHE_DIR }}
key: trivy-${{ runner.os }}-${{ steps.trivy-cache-key.outputs.date }}
restore-keys: |
trivy-${{ runner.os }}-
- name: Create report directory
run: mkdir -p "${TRIVY_REPORT_DIR}"
- name: Scan built Java artifacts
uses: aquasecurity/trivy-action@v0.36.0
with:
scan-type: rootfs
scan-ref: ${{ env.TRIVY_ARTIFACT_SCAN_DIR }}
scanners: vuln
format: json
output: ${{ env.TRIVY_REPORT_DIR }}/java-vuln-results.json
severity: ${{ env.TRIVY_SEVERITY }}
ignore-unfixed: true
exit-code: 0
- name: Scan source, IaC, secrets, and licenses
run: |
trivy fs . \
--scanners secret,misconfig,license \
--skip-dirs "${TRIVY_SOURCE_SKIP_DIRS}" \
--severity "${TRIVY_SEVERITY}" \
--ignore-unfixed \
--format json \
--output "${TRIVY_REPORT_DIR}/source-results.json" \
--exit-code 0
- name: Convert Java reports
run: |
trivy convert --format sarif --output "${TRIVY_REPORT_DIR}/java-vuln-results.sarif" --scanners vuln "${TRIVY_REPORT_DIR}/java-vuln-results.json"
trivy convert --format table --output "${TRIVY_REPORT_DIR}/java-vuln-results.txt" --scanners vuln "${TRIVY_REPORT_DIR}/java-vuln-results.json"
- name: Convert source reports
run: |
trivy convert --format sarif --output "${TRIVY_REPORT_DIR}/source-results.sarif" --scanners secret,misconfig,license "${TRIVY_REPORT_DIR}/source-results.json"
trivy convert --format table --output "${TRIVY_REPORT_DIR}/source-results.txt" --scanners secret,misconfig,license "${TRIVY_REPORT_DIR}/source-results.json"
- name: Generate readable Java report
run: node scripts/trivy-readable-report.mjs --input "${TRIVY_REPORT_DIR}/java-vuln-results.json" --output "${TRIVY_REPORT_DIR}/java-readable-report.md" --root .
- name: Generate SARIF HTML reports
run: |
python -m pip install --disable-pip-version-check sarif-tools
sarif html "${TRIVY_REPORT_DIR}/java-vuln-results.sarif" --output "${TRIVY_REPORT_DIR}/java-sarif-report.html"
sarif html "${TRIVY_REPORT_DIR}/source-results.sarif" --output "${TRIVY_REPORT_DIR}/source-sarif-report.html"
- name: Expose Trivy report outputs
id: report-outputs
run: |
node <<'EOF'
const fs = require('node:fs');
const reportDir = process.env.TRIVY_REPORT_DIR;
const output = process.env.GITHUB_OUTPUT;
const summary = process.env.GITHUB_STEP_SUMMARY;
const javaReport = JSON.parse(fs.readFileSync(`${reportDir}/java-vuln-results.json`, 'utf8'));
const sourceReport = JSON.parse(fs.readFileSync(`${reportDir}/source-results.json`, 'utf8'));
const counts = {
CRITICAL: 0,
HIGH: 0,
MEDIUM: 0,
LOW: 0,
UNKNOWN: 0
};
for (const result of javaReport.Results ?? []) {
for (const vulnerability of result.Vulnerabilities ?? []) {
const severity = vulnerability.Severity ?? 'UNKNOWN';
counts[severity] = (counts[severity] ?? 0) + 1;
}
}
const sourceCounts = {
misconfigurations: 0,
secrets: 0,
licenses: 0
};
for (const result of sourceReport.Results ?? []) {
sourceCounts.misconfigurations += (result.Misconfigurations ?? []).length;
sourceCounts.secrets += (result.Secrets ?? []).length;
sourceCounts.licenses += (result.Licenses ?? []).length;
}
const total = Object.values(counts).reduce((sum, count) => sum + count, 0);
const outputs = {
report_artifact: 'trivy-reports',
json_report: `${reportDir}/java-vuln-results.json`,
sarif_report: `${reportDir}/java-vuln-results.sarif`,
text_report: `${reportDir}/java-vuln-results.txt`,
readable_report: `${reportDir}/java-readable-report.md`,
java_json_report: `${reportDir}/java-vuln-results.json`,
java_sarif_report: `${reportDir}/java-vuln-results.sarif`,
java_text_report: `${reportDir}/java-vuln-results.txt`,
java_readable_report: `${reportDir}/java-readable-report.md`,
java_sarif_html_report: `${reportDir}/java-sarif-report.html`,
source_json_report: `${reportDir}/source-results.json`,
source_sarif_report: `${reportDir}/source-results.sarif`,
source_text_report: `${reportDir}/source-results.txt`,
source_sarif_html_report: `${reportDir}/source-sarif-report.html`,
total_vulnerabilities: total,
critical_vulnerabilities: counts.CRITICAL,
high_vulnerabilities: counts.HIGH,
medium_vulnerabilities: counts.MEDIUM,
low_vulnerabilities: counts.LOW,
unknown_vulnerabilities: counts.UNKNOWN,
source_misconfigurations: sourceCounts.misconfigurations,
source_secrets: sourceCounts.secrets,
source_license_findings: sourceCounts.licenses
};
fs.appendFileSync(output, Object.entries(outputs).map(([key, value]) => `${key}=${value}`).join('\n') + '\n');
fs.appendFileSync(summary, [
'## Trivy Report Outputs',
'',
'| Output | Value |',
'| --- | --- |',
...Object.entries(outputs).map(([key, value]) => `| \`${key}\` | \`${value}\` |`),
''
].join('\n'));
EOF
- name: Upload Java SARIF to code scanning
uses: github/codeql-action/upload-sarif@v4
if: always() && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
with:
sarif_file: ${{ env.TRIVY_REPORT_DIR }}/java-vuln-results.sarif
category: trivy-java-artifacts
- name: Upload source SARIF to code scanning
uses: github/codeql-action/upload-sarif@v4
if: always() && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
with:
sarif_file: ${{ env.TRIVY_REPORT_DIR }}/source-results.sarif
category: trivy-source
- name: Upload Trivy reports
uses: actions/upload-artifact@v4
if: always()
with:
name: trivy-reports
path: ${{ env.TRIVY_REPORT_DIR }}
retention-days: 14