PI-1877 Create initial project for manage-supervision-and-oasys (#3211) #592
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Security | |
on: | |
schedule: | |
- cron: "30 5 * * MON-FRI" # Every weekday at 05:30 UTC | |
workflow_dispatch: | |
push: | |
branches: | |
- main | |
paths: | |
- '**/.trivyignore' | |
jobs: | |
get-projects: | |
runs-on: ubuntu-latest | |
outputs: | |
projects: ${{ steps.get-projects.outputs.projects }} | |
steps: | |
- uses: actions/checkout@v4 | |
- id: get-projects | |
run: echo "projects=$(find projects -mindepth 1 -maxdepth 1 -printf "%f\n" | jq --raw-input . | jq --slurp --compact-output .)" | tee -a "$GITHUB_OUTPUT" | |
trivy-scan: | |
runs-on: ubuntu-latest | |
needs: | |
- get-projects | |
strategy: | |
fail-fast: false | |
matrix: | |
project: ${{ fromJson(needs.get-projects.outputs.projects) }} | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Scan image | |
uses: aquasecurity/trivy-action@d43c1f16c00cfd3978dde6c07f4bbcf9eb6993ca # v0.16.1 | |
with: | |
image-ref: 'ghcr.io/ministryofjustice/hmpps-probation-integration-services/${{ matrix.project }}:latest' | |
ignore-unfixed: true | |
severity: 'CRITICAL,HIGH' | |
exit-code: '0' | |
format: 'sarif' | |
output: 'trivy-results.sarif' | |
trivyignores: '.trivyignore,projects/${{ matrix.project }}/.trivyignore' | |
limit-severities-for-sarif: true | |
- name: Upload Trivy scan results to GitHub Security tab | |
uses: github/codeql-action/upload-sarif@v3 | |
if: always() | |
with: | |
sarif_file: 'trivy-results.sarif' | |
- name: Get Trivy results | |
uses: aquasecurity/trivy-action@d43c1f16c00cfd3978dde6c07f4bbcf9eb6993ca # v0.16.1 | |
with: | |
image-ref: 'ghcr.io/ministryofjustice/hmpps-probation-integration-services/${{ matrix.project }}:latest' | |
ignore-unfixed: true | |
severity: 'CRITICAL,HIGH' | |
format: 'json' | |
output: 'results.json' | |
trivyignores: '.trivyignore,projects/${{ matrix.project }}/.trivyignore' | |
- name: Output results | |
id: results | |
run: echo "vulnerabilities=$(jq -c '.Results[].Vulnerabilities | select(. != null) | flatten' results.json)" | tee -a "$GITHUB_OUTPUT" | |
env: | |
GITHUB_TOKEN: ${{ github.token }} | |
- name: Merge outputs | |
uses: cloudposse/github-action-matrix-outputs-write@928e2a2d3d6ae4eb94010827489805c17c81181f # v0.4.2 | |
with: | |
matrix-step-name: trivy | |
matrix-key: ${{ matrix.project }} | |
outputs: | | |
vulnerabilities: ${{ steps.results.outputs.vulnerabilities }} | |
trivy-merge: | |
runs-on: ubuntu-latest | |
needs: | |
- trivy-scan | |
steps: | |
- uses: actions/checkout@v4 | |
- uses: cloudposse/github-action-matrix-outputs-read@ea1c28d66c34b8400391ed74d510f66abc392d5e # v0.1.1 | |
id: trivy | |
with: | |
matrix-step-name: trivy | |
- name: Create GitHub issues | |
run: | | |
echo "$result" | jq -c '[.vulnerabilities | to_entries[] | .key as $project | .value // empty | map(. + {Projects: [$project]})] | |
| flatten | |
| group_by(.VulnerabilityID) | |
| map(reduce .[] as $vuln (.[0] + {Locations:[]}; .Projects += $vuln.Projects | .Locations += [$vuln.PkgName + ":" + $vuln.InstalledVersion + " (" + $vuln.PkgPath + ")"])) | |
| map_values({Title: .VulnerabilityID, Body: ("### " + .Title + "\n" + .PrimaryURL + "\n>" + .Description + "\n#### Projects:\n* " + (.Projects | sort | unique | join("\n* ")) + "\n#### Locations:\n* `" + (.Locations | sort | unique | join("`\n* `")) + "`\n#### References:\n* " + (.References | sort | unique | join("\n* ")))}) | |
| .[]' \ | |
| while read -r vulnerability; do | |
title=$(echo "$vulnerability" | jq -r '.Title') | |
body=$(echo "$vulnerability" | jq -r '.Body') | |
existing=$(gh issue list --state open --label dependencies --label security --search "$title" --json 'number' --jq '.[].number' | head -n1) | |
if [ -n "$existing" ]; then | |
echo "Issue '$title' already exists, updating body..." | |
gh issue edit "$existing" --body "$body" | |
else | |
gh issue create --title "$title" --body "$body" --label 'dependencies,security' | |
fi | |
done | |
env: | |
GITHUB_TOKEN: ${{ github.token }} | |
result: ${{ steps.trivy.outputs.result }} | |
- name: Close GitHub issues | |
run: | | |
openissues="$(gh issue list --state open --label dependencies --label security | awk '{print $3}')" | |
scanresults="$(echo "$result" | jq -r -c '.vulnerabilities | with_entries(select(.value != null)) | .[][].VulnerabilityID' | sort -u)" | |
issuestoclose="$(comm -23 <(echo "$openissues" | sort -u) <(echo "$scanresults" | sort -u))" #print lines only present in first file | |
echo "openissues=$openissues" | |
echo "scanresults=$scanresults" | |
echo "issuestoclose=$issuestoclose" | |
for cve in $issuestoclose; do | |
echo "$cve is already resolved, removing matching issue..." | |
issuenumber=$(gh issue list --state open --label dependencies --label security --search "$cve" | awk '{print $1}') | |
echo "$issuenumber" | xargs -n1 gh issue close | |
done | |
env: | |
GITHUB_TOKEN: ${{ github.token }} | |
result: ${{ steps.trivy.outputs.result }} | |
- name: Fail job if any vulnerabilities are found | |
if: steps.trivy.outputs.result != '{}' | |
run: if [ "$(echo "$result" | jq '.vulnerabilities | with_entries(select(.value != null)) | length')" != 0 ]; then exit 1; fi | |
env: | |
result: ${{ steps.trivy.outputs.result }} | |
veracode-scan: | |
runs-on: ubuntu-latest | |
needs: | |
- get-projects | |
steps: | |
- uses: actions/checkout@v4 | |
- uses: actions/setup-java@v4 | |
with: | |
java-version: 21 | |
distribution: temurin | |
- uses: gradle/actions/setup-gradle@v3 | |
with: | |
cache-read-only: true | |
- name: Build jars | |
run: ./gradlew jar | |
- name: Package jars | |
run: find . -name '*.jar' | zip -r package.zip -@ | |
- name: Upload to Veracode | |
uses: veracode/[email protected] | |
with: | |
appname: hmpps-probation-integration-services | |
createprofile: false | |
deleteincompletescan: 2 # force delete any incomplete scans | |
filepath: package.zip | |
vid: ${{ secrets.CYBERSECURITY_VERACODE_API_ID }} | |
vkey: ${{ secrets.CYBERSECURITY_VERACODE_API_KEY }} |