name: Vulnerability Scan on: push: branches: [ main, develop ] pull_request: branches: [ main, develop ] schedule: # Run weekly on Monday at 2 AM UTC - cron: '0 2 * * 1' workflow_dispatch: jobs: scan-go-dependencies: name: Scan Go Dependencies (govulncheck) runs-on: ubuntu-latest permissions: contents: read security-events: write steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v5 with: go-version: '1.24' check-latest: true - name: Install govulncheck run: | go install golang.org/x/vuln/cmd/govulncheck@latest - name: Run govulncheck id: vulncheck continue-on-error: true run: | echo "Scanning Go dependencies for vulnerabilities..." govulncheck ./... > vulncheck-report.txt 2>&1 echo "report<> $GITHUB_OUTPUT cat vulncheck-report.txt >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT - name: Upload govulncheck report uses: actions/upload-artifact@v4 if: always() with: name: govulncheck-report path: vulncheck-report.txt retention-days: 30 - name: Check for HIGH/CRITICAL vulnerabilities id: check-severity run: | if grep -iE "(HIGH|CRITICAL)" vulncheck-report.txt > /dev/null 2>&1; then echo "found_critical=true" >> $GITHUB_OUTPUT echo "❌ HIGH or CRITICAL vulnerabilities found!" cat vulncheck-report.txt exit 1 else echo "found_critical=false" >> $GITHUB_OUTPUT echo "✅ No HIGH or CRITICAL vulnerabilities found" if [ -s vulncheck-report.txt ]; then echo "⚠️ Some vulnerabilities found (not HIGH/CRITICAL):" cat vulncheck-report.txt fi fi - name: Comment PR with results if: github.event_name == 'pull_request' && always() uses: actions/github-script@v7 with: script: | const fs = require('fs'); let report = 'No vulnerabilities found'; if (fs.existsSync('vulncheck-report.txt')) { report = fs.readFileSync('vulncheck-report.txt', 'utf8'); } const body = `## 🔒 Go Dependency Vulnerability Scan Results \`\`\` ${report} \`\`\` ${steps.check-severity.outputs.found_critical === 'true' ? '❌ **HIGH or CRITICAL vulnerabilities detected**' : '✅ **No HIGH or CRITICAL vulnerabilities**'} `; github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: body }); scan-docker-image: name: Scan Docker Image (Trivy) runs-on: ubuntu-latest if: github.event_name != 'schedule' || github.event_name == 'workflow_dispatch' permissions: contents: read security-events: write steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Build Docker image uses: docker/build-push-action@v5 with: context: . push: false tags: veza-backend-api:scan cache-from: type=gha cache-to: type=gha,mode=max - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@master with: image-ref: 'veza-backend-api:scan' format: 'sarif' output: 'trivy-results.sarif' severity: 'CRITICAL,HIGH' exit-code: '1' ignore-unfixed: false - name: Upload Trivy results to GitHub Security uses: github/codeql-action/upload-sarif@v3 if: always() with: sarif_file: 'trivy-results.sarif' - name: Upload Trivy report uses: actions/upload-artifact@v4 if: always() with: name: trivy-report path: trivy-results.sarif retention-days: 30 - name: Run Trivy with table output continue-on-error: true run: | docker run --rm \ -v /var/run/docker.sock:/var/run/docker.sock \ aquasec/trivy:latest image veza-backend-api:scan \ --format table \ --severity CRITICAL,HIGH scan-docker-image-report-only: name: Scan Docker Image (Report Only) runs-on: ubuntu-latest if: github.event_name == 'schedule' permissions: contents: read steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Build Docker image uses: docker/build-push-action@v5 with: context: . push: false tags: veza-backend-api:scan cache-from: type=gha cache-to: type=gha,mode=max - name: Run Trivy vulnerability scanner (Report Only) uses: aquasecurity/trivy-action@master with: image-ref: 'veza-backend-api:scan' format: 'table' exit-code: '0' severity: 'CRITICAL,HIGH,MEDIUM,LOW' ignore-unfixed: false - name: Upload Trivy report uses: actions/upload-artifact@v4 if: always() with: name: trivy-report-scheduled path: trivy-results.txt retention-days: 30