Trivy PR Gating is the mechanism that prevents code from being merged into your main branch if it introduces new, high-severity security vulnerabilities.

Here’s how it works in practice. Imagine you have a GitHub Actions workflow that runs Trivy on your pull request.

name: Trivy Security Scan

on:
  pull_request:
    branches:
      - main

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Trivy scan
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'vulnerability'
          ignore-unfixed: true
          format: 'json'
          output: 'trivy-results.json'
          severity: 'CRITICAL,HIGH' # Only scan for CRITICAL and HIGH

      - name: Upload Trivy scan results
        uses: actions/upload-artifact@v3
        with:
          name: trivy-results
          path: trivy-results.json

      - name: Check Trivy results for new vulnerabilities
        id: check_results
        run: |
          NEW_VULNERABILITIES=$(jq '[.Results[].Vulnerabilities[] | select(.Severity | test("CRITICAL|HIGH"))] | length' trivy-results.json)
          echo "Found $NEW_VULNERABILITIES new high/critical vulnerabilities."
          if [ "$NEW_VULNERABILITIES" -gt 0 ]; then
            echo "::error ::New high/critical vulnerabilities detected. Please fix them before merging."
            exit 1
          fi

When a pull request is opened, this workflow kicks off. Trivy scans the code changes. If it finds any new vulnerabilities classified as CRITICAL or HIGH that weren’t present in the main branch, the check_results step will detect them. Because exit 1 is called, the workflow fails, and GitHub will show a red X on the pull request, blocking the merge. If no new high/critical vulnerabilities are found, the workflow passes, and the merge is allowed.

The core problem Trivy PR Gating solves is the introduction of new, significant security risks into your codebase via merged pull requests. Without it, developers might unknowingly merge code that opens up your application to known exploits, leading to potential data breaches, system downtime, or compliance failures. This gating acts as an automated, early-stage defense, ensuring that security is considered before code becomes part of the main release.

Internally, Trivy operates by scanning your project’s dependencies (e.g., package.json, requirements.txt, Gemfile, container images) and comparing the versions of libraries and packages against known vulnerability databases like CVE (Common Vulnerabilities and Exposures). The trivy-action for GitHub Actions is a wrapper that automates this process within your CI/CD pipeline. It takes configuration like scan-type, severity, and ignore-unfixed, then executes Trivy. The crucial part for gating is the severity parameter, which allows you to define the threshold for what constitutes a blocking issue. The subsequent run script in the GitHub Actions workflow then parses Trivy’s JSON output to count any findings that meet your defined severity criteria.

The real power comes from the severity parameter and the subsequent script. By setting severity: 'CRITICAL,HIGH', you’re telling Trivy to only report on the most impactful vulnerabilities. The jq command then specifically looks for vulnerabilities within the Results array that match these severities. If the count (length) of such vulnerabilities is greater than zero, the script exits with a non-zero status code, failing the CI job and blocking the merge. The ignore-unfixed: true is often used in conjunction to avoid blocking merges due to vulnerabilities in dependencies that have no available patch yet, allowing you to focus on actionable findings.

A common point of confusion is how Trivy determines new vulnerabilities. It doesn’t just scan the PR code in isolation. The trivy-action (and Trivy itself when configured for PRs) often compares the findings in the PR against the state of the main branch. This ensures you’re only alerted to vulnerabilities introduced by the specific changes in the PR, not those that already existed in the target branch. This comparison is key to preventing "alert fatigue" and ensuring the gate is focused on regressions.

The next logical step after implementing PR gating is to integrate a Software Bill of Materials (SBOM) generation into your pipeline, perhaps using trivy fs --format spdx-json --output sbom.json . This provides a comprehensive inventory of all components in your application, which is crucial for understanding your attack surface and for compliance with evolving regulations.

Want structured learning?

Take the full Trivy course →