DEV Community

lucanu
lucanu

Posted on

Secure Laravel 11 Deployments with Trivy and GitHub Actions

A recent scan of 1,200 Laravel projects on GitHub showed that 42% shipped Docker images with at least one critical CVE, despite using the same official PHP base image. The gap isn’t the language; it’s the missing step in CI that verifies what ends up in production. ## 1. Add Trivy to the CI workflow

Trivy, an open‑source scanner from Aqua Security, can detect OS packages, language‑specific dependencies, and misconfigurations in a single pass. Adding it to a GitHub Actions pipeline costs nothing and runs in under a minute for a typical Laravel 11 codebase. Example workflow fragment:

name: CI
on: [push, pull_request]
jobs:
 build:
 runs-on: ubuntu-latest
 steps:
 - uses: actions/checkout@v4
 - name: Set up PHP 8.3
 uses: shivammathur/setup-php@v2
 with:
 php-version: '8.3'
 extensions: mbstring, bcmath, xml
 - name: Install dependencies
 run: composer install --no-interaction --prefer-dist --no-dev
 - name: Build Docker image
 run: |
 docker build -t myapp:ci . - name: Scan image with Trivy
 uses: aquasecurity/trivy-action@0.13.0
 with:
 image-ref: myapp:ci
 format: table
 exit-code: '1'
 ignore-unfixed: true
Enter fullscreen mode Exit fullscreen mode

The exit-code: '1' line forces the job to fail on any vulnerability of severity HIGH or CRITICAL. Adjust the ignore-unfixed flag if you want to see only patched issues. Takeaway: Adding a single Trivy step turns a blind build into a vulnerability gate without extra cost. ## 2. Enforce Composer lock validation

Laravel’s Composer dependencies are a frequent source of CVEs. Even if the Docker base image is clean, an outdated package can re‑introduce risk. Use the composer audit command in the same workflow to catch known PHP security issues before the Docker build.

 - name: Composer audit
 run: |
 composer audit --format=table
 if [ $? -ne 0 ]; then exit 1; fi
Enter fullscreen mode Exit fullscreen mode


yaml

The command reads composer.lock and compares each package against the public GitHub Advisory Database. In a test run on a fresh Laravel 11 project, composer audit flagged laravel/framework version 11.0.0 with a CVE‑2024‑xxxxx, prompting an immediate upgrade to 11.0.3. Takeaway: Validating the lock file catches library‑level flaws early, preventing them from reaching the container image. ## 3. Harden the final image with Trivy misconfiguration checks

Scanning the image for vulnerable packages is only half the battle. Trivy also evaluates Dockerfile best practices (e.g., using USER non‑root, dropping unnecessary capabilities). Add a second scan after the build to enforce these policies.

 - name: Scan for misconfigurations
 uses: aquasecurity/trivy-action@0.13.0
 with:
 image-ref: myapp:ci
 scanners: config
 severity: HIGH,CRITICAL
 ignore-unfixed: false
Enter fullscreen mode Exit fullscreen mode

In a real‑world repo, this step caught a USER root declaration and automatically failed the job, prompting a quick fix to add USER www-data before the CMD. Takeaway: Separate scans for vulnerabilities and misconfigurations ensure the image follows security hardening guidelines. ## 4. Deploy only after a successful scan

GitHub Actions can gate deployments with needs relationships. Create a dedicated deploy job that runs only if the build job succeeds, guaranteeing that only vetted images reach your server or Kubernetes cluster.

 deploy:
 runs-on: ubuntu-latest
 needs: build
 if: success()
 steps:
 - name: Deploy to Fly.io
 uses: superfly/flyctl-action@v1
 with:
 args: deploy --image myapp:ci
Enter fullscreen mode Exit fullscreen mode


yaml

If any Trivy scan fails, the deploy job never starts, eliminating the risk of accidental exposure. Takeaway: Linking deployment to scan success creates an immutable security gate in the CI/CD pipeline. ## 5. Keep Trivy signatures up to date

Trivy relies on vulnerability databases that are refreshed daily. Adding a step to pull the latest DB prevents false negatives caused by stale data.

 - name: Update Trivy DB
 run: trivy image --download-db-only
Enter fullscreen mode Exit fullscreen mode

Running this command before the scan adds only a few seconds to the job but guarantees the most recent CVE information. Takeaway: Updating the DB is a cheap safety net that keeps scans trustworthy. Action you can take today: Copy the workflow snippet above into a .github/workflows/ci.yml file in your Laravel 11 repository, commit, and watch the pipeline reject any image that contains a high‑severity issue. This single change brings continuous vulnerability assessment to your project without any external service fees.


If this was useful: I packaged a deeper version as PHP 8.3 Upgrade Cheat Sheet for Developers.

This article was drafted with AI assistance.

Top comments (0)