Composer Audit: Find and Fix PHP Dependency Vulnerabilities
Learn how to run composer audit to detect security vulnerabilities in your PHP dependencies, fix them with composer update, and automate audits in your CI/CD pipeline.
The average Laravel application pulls in 80 to 120 Composer packages. Each one is a potential entry point for attackers if a vulnerability is disclosed and you do not update in time. In 2025 alone, over 400 PHP package vulnerabilities were added to the Security Advisories Database.
composer audit is the built-in tool for catching these before they reach production. This guide covers everything you need to know: how it works, how to fix what it finds, how to automate it in CI/CD, and how it compares to other tools like Dependabot.
What composer audit does
Composer audit was introduced in Composer 2.4. It reads your composer.lock file and checks every installed package against the PHP Security Advisories Database, which aggregates CVEs from the National Vulnerability Database, GitHub Security Advisories, and package maintainer reports.
composer audit
Here is what typical output looks like:
Found 2 security vulnerability advisories affecting 2 packages:
+-------------------+------------------------------+
| Package | symfony/http-foundation |
| CVE | CVE-2024-5678 |
| Title | Request validation bypass |
| URL | https://... |
| Affected versions | >=5.0.0, <5.4.5 |
| Reported at | 2024-03-15T00:00:00+00:00 |
+-------------------+------------------------------+
+-------------------+------------------------------+
| Package | guzzlehttp/guzzle |
| CVE | CVE-2023-9101 |
| Title | SSRF in HTTP request handler |
| URL | https://... |
| Affected versions | >=6.0.0, <6.5.3 |
| Reported at | 2023-11-02T00:00:00+00:00 |
+-------------------+------------------------------+
If no vulnerabilities are found, it returns exit code 0. If vulnerabilities exist, it returns a non-zero exit code, which makes it easy to use as a gate in automated pipelines.
How to fix vulnerabilities
Option 1: Update specific packages
The safest approach is to update only the affected packages:
composer update symfony/http-foundation guzzlehttp/guzzle
This updates just those packages (and their dependencies) to the latest versions allowed by your composer.json constraints. Run composer audit again to confirm the fix.
Option 2: Update all packages
If you are comfortable updating everything:
composer update
This is faster but carries more risk of breaking changes. Always run your test suite after a bulk update.
Option 3: When you cannot update
Sometimes a fix requires a major version bump that would break your application. In that case:
- Check if the vulnerability actually affects your usage. Read the CVE description carefully. A vulnerability in a feature you do not use may be lower priority.
- If you must stay on the current version, consider adding a workaround (input validation, WAF rule, or middleware) while you plan the upgrade.
- Document the decision. A suppressed vulnerability should be a conscious, tracked choice, not something that gets forgotten.
Handling version constraints
If composer update does not resolve a vulnerability, your composer.json constraints may be too tight. For example, if you require "symfony/http-foundation": "^5.4" and the fix is in 6.0, you need to update the constraint:
{
"require": {
"symfony/http-foundation": "^6.0"
}
}
Then run composer update symfony/http-foundation and test thoroughly, since major version bumps often include breaking changes.
Output formats
Composer audit supports multiple output formats for different use cases:
# Human-readable table (default)
composer audit
# JSON for CI/CD parsing and dashboards
composer audit --format=json
# Plain text for simple scripts
composer audit --format=plain
# Only check production dependencies
composer audit --no-dev
The JSON format is particularly useful for integrating with security dashboards or Slack notifications:
composer audit --format=json | jq '.advisories | length'
# Returns: 2
Automating composer audit in CI/CD
GitHub Actions
Add this to your workflow to block PRs and deployments with known vulnerabilities:
name: Security Audit
on:
push:
branches: [main, develop]
pull_request:
schedule:
- cron: '0 8 * * 1' # Every Monday at 8am
jobs:
security-audit:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
- name: Install dependencies
run: composer install --no-interaction --prefer-dist
- name: Run security audit
run: composer audit --format=json
The schedule trigger is important. New CVEs are disclosed constantly, and a Monday morning audit catches anything published over the weekend, even if no code changed.
GitLab CI
security-audit:
stage: test
script:
- composer install --no-interaction --prefer-dist
- composer audit
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_PIPELINE_SOURCE == "schedule"'
Bitbucket Pipelines
pipelines:
default:
- step:
name: Security Audit
caches:
- composer
script:
- composer install --no-interaction --prefer-dist
- composer audit
Composer audit vs Dependabot vs Snyk
| Feature | composer audit | Dependabot | Snyk |
|---|---|---|---|
| Cost | Free (built-in) | Free (GitHub) | Free tier / paid |
| When it runs | On demand / CI | Continuously | Continuously |
| Auto-fix PRs | No | Yes | Yes |
| Database | PHP Security Advisories | GitHub Advisory Database | Snyk Vulnerability DB |
| PHP-specific | Yes | Yes | Yes |
| License scanning | No | No | Yes |
| Container scanning | No | No | Yes |
Our recommendation: Use them together. Composer audit in CI catches vulnerabilities at deploy time. Dependabot catches them between deployments and opens fix PRs automatically. They complement each other.
Setting up Dependabot for PHP
Create .github/dependabot.yml in your repository:
version: 2
updates:
- package-ecosystem: "composer"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
Real-world example: Responding to a critical CVE
Here is a typical workflow when a critical vulnerability is disclosed:
- Your Monday morning CI schedule runs
composer auditand fails - You check the output and find a critical CVE in a package you use
- You run
composer update package/namelocally - You run your test suite to verify nothing breaks
- You push the fix and the audit passes
- Production is updated within hours of the disclosure
For a recent real-world example, see our post on the Livewire RCE vulnerability (CVE-2025-54068), which was a critical remote code execution flaw that composer audit would have flagged immediately after the advisory was published.
Common issues and solutions
"No lock file found"
Composer audit requires a composer.lock file. If you do not commit your lock file (you should), generate one first:
composer install
composer audit
Old Composer version
Composer audit requires version 2.4 or later. Check your version and upgrade if needed:
composer --version
composer self-update
False sense of security
Composer audit only checks against known, disclosed vulnerabilities. It cannot find zero-day exploits or vulnerabilities that have not been reported to the database yet. It is one layer of defense, not the only one.
For a more complete picture of your application's security posture, combine composer audit with:
- Security header checks to catch missing HTTP security headers
- External attack surface scanning to find exposed files, debug endpoints, and misconfigured DNS
- The Laravel Security Checklist for a comprehensive 30-point audit
Summary
| What | Command |
|---|---|
| Run a basic audit | composer audit |
| JSON output for CI | composer audit --format=json |
| Skip dev dependencies | composer audit --no-dev |
| Fix specific packages | composer update package/name |
| Fix all packages | composer update |
| Check Composer version | composer --version |
Run it locally. Run it in CI. Run it on a schedule. The five seconds it takes to run composer audit is nothing compared to the cost of deploying a known vulnerability to production.
Want continuous monitoring beyond just dependencies? Run a free StackShield scan to check your full external attack surface, including security headers, exposed files, DNS configuration, and more.
Frequently Asked Questions
What does composer audit do?
Composer audit scans your composer.lock file against the PHP Security Advisories Database and reports any packages with known vulnerabilities. It shows the CVE identifier, severity level, affected version, and the version that contains the fix.
How do I fix vulnerabilities found by composer audit?
Run composer update followed by the package names listed in the audit output. For example: composer update symfony/http-foundation guzzlehttp/guzzle. If a major version upgrade is needed and could break your code, pin to the latest safe minor version in your composer.json first, then run composer update.
Should I run composer audit in CI/CD?
Yes. Adding composer audit to your CI/CD pipeline ensures no deployment goes out with known-vulnerable dependencies. You can use the --format=json flag for machine-readable output and fail the build on any findings. Most teams add it alongside their test suite so it runs on every pull request.
What is the difference between composer audit and Dependabot?
Composer audit is a local CLI command that checks your lock file on demand or in CI. Dependabot is a GitHub service that monitors your repository continuously and opens pull requests when new vulnerabilities are disclosed. They complement each other: composer audit catches issues at deploy time, while Dependabot catches them between deployments.
Can composer audit check dev dependencies?
Yes, composer audit checks all dependencies in your lock file by default, including dev dependencies. You can exclude dev packages by running composer audit --no-dev if you only want to audit production dependencies.
Related Articles
Laravel Debug Mode in Production: Why It's Dangerous and How to Fix It
Debug mode in production exposes stack traces, database credentials, environment variables, and internal paths. Learn exactly what it reveals, how attackers use it, and how to make sure it never reaches production.
SecurityOWASP Top 10 for Laravel: A Practical Guide
A hands-on mapping of every OWASP Top 10 (2021) category to specific Laravel vulnerabilities, with code examples of what goes wrong and how to fix it.
SecurityIs Your Laravel .env File Exposed? How to Check and Fix It
Your .env file contains database credentials, API keys, and encryption secrets. If it's accessible from the web, attackers already have everything they need. Here's how to check and fix it.
Compare StackShield
Security Checklists
Laravel Production Deployment Security Checklist
A comprehensive security checklist for deploying Laravel applications to production. Covers environment config, server hardening, access control, and monitoring.
20 itemsLaravel API Security Checklist
Secure your Laravel API endpoints against common vulnerabilities. Covers authentication, input validation, rate limiting, and response security.
Stay Updated on Laravel Security
Get actionable security tips, vulnerability alerts, and best practices for Laravel apps.