Composer Audit PHP Dependencies: Security Scanning for Vulnerabilities

Published:

Think running composer update is good enough? Think again.
Composer audit scans your installed PHP packages against Packagist/GitHub advisories and spits vulnerabilities straight to your terminal.
It ships with Composer 2.4+ and exits non-zero when it finds issues, so you can fail builds in CI.
Run it before you push, after dependency changes, and schedule it in pipelines to catch problems early.
This post shows how to run a full audit, read the output, and fix or automate remediations so you stop shipping avoidable security bugs.

Running a Full Composer Audit to Check PHP Dependencies for Vulnerabilities

DZekMKZrUm21zVdYDNdqZg

Composer’s composer audit command scans your installed PHP packages against known security advisories and spits out vulnerabilities right in your terminal. This feature landed in Composer 2.4 back in summer 2022. It queries Packagist’s Security Advisory API, which pulls data from GitHub Security Advisories and the FriendsOfPHP/security-advisories repository. You should be running this regularly. Before you push code, after updating dependencies, and automatically in CI/CD to catch problems early.

You’ll need Composer 2.4 or later. Running something older? Update first with composer self-update. When you fire up composer audit, Composer checks the versions in your vendor/ directory (or composer.lock) against the advisory database and returns a non-zero exit code equal to the number of vulnerabilities it finds. Zero exit code? You’re clean. This exit code behavior makes it perfect for automated enforcement. Your pipeline can kill the build if vulnerabilities show up.

Here’s how to run a full audit:

  1. Update Composer to at least version 2.4: composer self-update
  2. Navigate to your project directory where composer.json and composer.lock live.
  3. Run the audit command: composer audit
  4. Review the terminal output. It’ll list any advisories with package names, affected version ranges, CVE identifiers, and links to the full details.
  5. Check the exit code by running echo $? (Linux/macOS) or echo %errorlevel% (Windows) right after. Non-zero means vulnerabilities were found.

By default, composer audit shows results in table format. You can switch this with --format=json for machine-readable output or --format=plain for simple text. For example, if you install guzzlehttp/guzzle version 7.4.4, you’ll get a vulnerability warning like “Found 1 security vulnerability advisory affecting 1 package” and it’ll tell you to run composer audit for the complete picture.

Understanding Composer Audit Output for PHP Dependency Security

Uq_Yd1pmWai9XbA1hsd1NQ

When you run composer audit, you get the full advisory report with everything. If you ran composer update, composer require, or composer install --audit, you saw a shortened version that only shows advisory IDs and affected version ranges. Those abbreviated results come from package metadata and don’t actually hit the Security Advisory API. Want complete details including CVE identifiers, advisory titles, publication dates, and clickable links? Always run the standalone composer audit command.

Each line in the full audit output describes one vulnerability. You’ll see the package name (like symfony/http-kernel), a CVE identifier if one exists (like CVE-2023-12345), a short title summarizing the issue, a URL to the full advisory (usually on GitHub Security Advisories or the FriendsOfPHP repo), the affected version range, and the date the advisory was published or updated. Severity info might show up in the advisory title or in the linked report. Need to parse this data in a script? Use --format=json to get structured output with fields like advisoryId, packageName, affectedVersions, cve, and link.

Field Description Example
Package Fully qualified package name guzzlehttp/guzzle
CVE Common Vulnerabilities and Exposures identifier (if assigned) CVE-2022-29248
Affected Versions Version constraint showing vulnerable releases >=7.0.0,<7.4.5
Advisory Link URL to full write-up and mitigation steps https://github.com/advisories/GHSA-xxxx-yyyy-zzzz

Updating and Remediating Vulnerable Composer PHP Dependencies

5-fVNf1MUk6QU99dQjJkNw

Once composer audit flags a package, you need to update it to a safe version. Check the “Affected Versions” range in the audit output to see which releases are vulnerable, then look for a newer version outside that range. You can update a single package with composer update vendor/package or adjust the version constraint in composer.json and run composer update. Say guzzlehttp/guzzle 7.4.4 is vulnerable and 7.4.5 fixes it. Update your constraint to "guzzlehttp/guzzle": "^7.4.5" and run composer update guzzlehttp/guzzle.

Use composer outdated to see which packages have newer releases. This command lists your installed version, the latest version your constraints allow, and the latest version overall. It doesn’t check for vulnerabilities. It just shows available upgrades. Pair it with composer audit to both identify security issues and find safe upgrade targets. Watch out for semantic versioning. A caret constraint like ^2.3 allows updates to 2.x but blocks 3.0, which reduces breaking changes. If the vulnerability’s in a transitive dependency (a package required by one of your direct dependencies), you might need to update the parent package or use composer why vendor/package to trace where it’s coming from.

Remediation steps:

  • Update the package: composer update vendor/package to grab the latest allowed version.
  • Tighten the constraint: Edit composer.json to require a minimum safe version (like ^2.5.1) then run composer update.
  • Check transitive dependencies: Use composer why vendor/package to see which direct dependency pulls in the vulnerable package.
  • Replace abandoned packages: Composer 2.7+ warns about abandoned packages in audit output. Switch to the suggested replacement as soon as you can.
  • Force a newer version: If a parent package is slow to update, you can sometimes override with a higher minimum in your own composer.json.
  • Review breaking changes: Before jumping to a new major version, read the package’s changelog or upgrade guide so you don’t break things at runtime.

Integrating Composer Audit into CI/CD for Ongoing PHP Dependency Security

yruydOleXIm5yBfaTvfLjA

The best way to enforce dependency security is to run composer audit in every CI/CD pipeline and fail the build if it finds vulnerabilities. Because composer audit exits with a non-zero code when it discovers advisories, most CI systems (GitHub Actions, GitLab CI, Jenkins, CircleCI) will automatically mark the step as failed. You can run the check after installing dependencies with composer install --no-dev (to skip dev packages) and then run composer audit as a separate step, or use composer install --audit to get an abbreviated scan during install.

Set up a scheduled job to run audits even when you’re not actively deploying. A common pattern is running composer audit every two hours in a cron-triggered pipeline. For critical applications handling sensitive data or high traffic, consider running it every hour. This way, if a new advisory drops for a package you already have installed, you’ll know within a short window. Combine automated audits with alert notifications. Send Slack messages, email, or PagerDuty alerts when the audit step fails so your team can respond fast.

CI/CD implementation steps:

  1. Add a dependency install step in your pipeline: composer install --no-dev --prefer-dist
  2. Run the audit command: composer audit or use composer install --audit to get an abbreviated scan during install.
  3. Check the exit code: Most CI systems fail automatically on non-zero exit. No extra scripting needed.
  4. Optionally filter output format: Add --format=json if you want to parse results and send structured alerts.
  5. Schedule regular scans: Configure a cron job or scheduled pipeline to run composer audit every 1 to 2 hours.
  6. Set up notifications: Route audit failures to Slack, email, or incident management tools so the team gets alerted immediately.

Differences Between Composer Audit and Composer Outdated for PHP Projects

c8Xcpu4VSKaHLxI4xiV1Q

composer audit and composer outdated do different jobs and you should use both for complete dependency hygiene. composer audit checks your installed packages against a vulnerability database and reports security advisories. It only cares about known exploits and won’t tell you about newer versions unless they’re relevant to fixing a CVE. composer outdated lists every package that has a newer release, showing your current version, the latest version your constraints allow, and the absolute latest version. It doesn’t mention security at all. It’s purely a version-tracking tool.

Run composer outdated regularly to stay current with bug fixes, performance improvements, and new features. Then run composer audit to catch any packages with disclosed vulnerabilities. If a package shows up in both lists, prioritize the security update first. composer audit tells you the issue is urgent, and composer outdated tells you which version to target.

Comparison:

  • Purpose: composer audit reports vulnerabilities. composer outdated shows available updates.
  • Data source: composer audit queries Packagist’s Security Advisory API. composer outdated reads package metadata from Packagist’s main API.
  • Exit code: composer audit exits non-zero when vulnerabilities are found. composer outdated always exits zero (it just displays information).
  • Use case: Use composer audit to enforce security policies in CI. Use composer outdated to plan maintenance and keep dependencies fresh.

Using Additional Tools and APIs Alongside Composer Audit for Stronger PHP Dependency Security

rsDNUuJQU5OmMs6yGho8OQ

While composer audit covers most use cases, you can layer in extra tools for defense in depth. The roave/security-advisories package is a popular complement. It’s a meta-package that defines conflict rules for every known vulnerable version in the FriendsOfPHP advisory database. Add it to your require-dev section and Composer will refuse to install or update to a vulnerable version. This blocks bad packages before they land in your vendor/ directory, while composer audit reports issues after installation. Think of roave/security-advisories as a preventive control and composer audit as a detective control.

The Symfony CLI includes a security:check command (formerly the standalone Local PHP Security Checker) that scans composer.lock and reports advisories. It’s useful as a second opinion or if you prefer Symfony’s output format. You can also bring in third-party services like Snyk, which scans your repository, monitors your dependencies continuously, and sends alerts when new CVEs drop. Snyk’s GitHub Action can run on every pull request and add inline comments with remediation advice.

Building custom tooling or dashboards? Query the Packagist Security Advisory API directly at https://packagist.org/api/security-advisories/. Pass a packages parameter with a list of package names and an optional updatedSince Unix timestamp to fetch only recent changes. The API returns JSON with advisory IDs, affected version ranges, and links to full reports. This is the same endpoint composer audit uses under the hood.

Tool Approach Best For
roave/security-advisories Conflict rules block vulnerable versions during install/update Preventing bad installs in development and CI
Symfony security:check Scans composer.lock and reports advisories Quick local checks and alternative CI validation
Snyk Continuous monitoring with alerts and PR comments Proactive alerts and cross-project visibility

Troubleshooting Issues When Running Composer Audit on PHP Dependencies

4Ql1RPkfV5mYms6PLLfYGw

If composer audit isn’t working right, start by checking your Composer version. The audit command and related flags were added in Composer 2.4. If you’re on 2.3 or earlier, run composer self-update and try again. Network issues are another common problem. The audit queries Packagist’s API over HTTPS, so if your CI runner or local machine is behind a restrictive firewall or proxy, the request might time out or fail silently. Check your network logs and make sure outbound HTTPS to packagist.org is allowed.

Sometimes you’ll see an advisory listed for a version that doesn’t match what you have installed. This happens when the advisory’s affected version range is written broadly or when you’re looking at the abbreviated audit output from composer install --audit instead of the full composer audit report. Always run the full command to get exact version constraints and links. If you think an advisory is a false positive, verify your installed version with composer show vendor/package and compare it against the advisory’s affected range. You can also clear Composer’s cache with composer clear-cache and re-run the audit to rule out stale metadata.

Troubleshooting steps:

  1. Upgrade Composer: Run composer self-update to make sure you’re on Composer 2.4 or later.
  2. Check network access: Verify your machine or CI runner can reach packagist.org over HTTPS.
  3. Clear the cache: Run composer clear-cache and try composer audit again.
  4. Run full audit instead of abbreviated: If you only saw advisory IDs during composer update, run composer audit to get complete details.
  5. Verify installed versions: Use composer show vendor/package to confirm the version and compare it against the advisory’s affected range to rule out false positives.

For edge cases like private packages or internal forks, remember that composer audit only knows about advisories published to Packagist or GitHub Security Advisories. If you maintain proprietary packages, you’ll need to publish your own advisories or use an internal scanning tool that understands your private repository structure.

Best Practices for Maintaining Secure PHP Dependencies with Composer Audit

smls2M2tVo2MJFjAcrXayA

Security is a habit, not a one-time task. Run composer audit locally before every commit and in CI on every push. Commit your composer.lock file to version control so everyone on the team and your CI pipeline installs the exact same versions. This makes audit results consistent and prevents “works on my machine” surprises. Set up scheduled scans (every 1 to 2 hours) to catch newly disclosed vulnerabilities even when you’re not actively deploying code.

Keep your dependencies lean. The fewer packages you depend on, the smaller your attack surface and the easier it is to audit and update. Review your require and require-dev sections regularly and remove anything you no longer use. When a package gets flagged as abandoned, replace it quickly. Composer 2.7+ will exit non-zero during audit if you depend on an abandoned package, and you can configure this behavior in composer.json if you need more flexibility.

Best practices:

  • Run composer audit in CI and fail builds on non-zero exit to enforce security gates.
  • Commit composer.lock so everyone uses the same versions and audit results are reproducible.
  • Schedule regular audits (every 1 to 2 hours) to catch new advisories between deployments.
  • Keep Composer itself updated with composer self-update to get the latest audit features and security fixes.
  • Use composer outdated alongside audit to find safe upgrade targets and keep dependencies current.
  • Minimize dependencies by removing unused packages and avoiding bloated libraries.
  • Replace abandoned packages as soon as Composer flags them. Don’t wait for a security issue.
  • Integrate roave/security-advisories in development to block vulnerable versions before they’re installed.

Final Words

In the action, we ran a full composer audit, parsed its output, and walked through safe updates and CI gating so vulnerabilities don’t slip into production.

You learned when to use Composer 2.4+, how to read CVE and severity details, and practical remediation steps for direct and transitive packages. The guide also covered troubleshooting and extra tools to strengthen scans.

Keep composer audit php dependencies in your regular workflow and CI; it catches issues early and makes upgrades less painful.

FAQ

Q: What does composer audit do and when should I use it?

A: The composer audit command checks your PHP dependencies for known vulnerabilities by querying Packagist’s Security Advisory API; use it after dependency changes, before releases, and in CI for continuous checks.

Q: What Composer version is required to run composer audit?

A: Composer audit requires Composer 2.4 or newer; the feature landed in summer 2022, so update with composer self-update before running if you’re on an older release.

Q: How do I run composer audit (quick steps)?

A: To run composer audit, ensure Composer 2.4+, cd to your project root, run composer audit, use –format=json for automation, and note non-zero exit codes equal the number of findings.

Q: What does a non-zero exit code from composer audit mean?

A: A non-zero exit code from composer audit means vulnerabilities were found; the exit code equals the count of reported issues, letting CI fail builds automatically.

Q: What information appears in composer audit output and when should I use –format=json?

A: Composer audit output includes package, CVE (if present), advisory title, link, affected ranges, and dates; use –format=json when you need machine-readable results for automation or triage.

Q: How should I remediate vulnerable packages reported by composer audit?

A: To remediate, run composer outdated to find updates, adjust version constraints, run composer update vendor/package safely, consider patching or swapping abandoned libraries, and retest before deploying.

Q: How do I handle transitive vulnerabilities found by composer audit?

A: To handle transitive vulnerabilities, run composer why to trace which package requires it, update the parent package, open an upstream PR, or vendor/replace the transitive dependency if needed.

Q: How can I integrate composer audit into CI/CD pipelines?

A: Integrate composer audit by running it in your pipeline, fail on non-zero exit codes, schedule regular scans (hourly or every few hours for critical apps), and use –format=json for alerts.

Q: What’s the difference between composer audit and composer outdated?

A: Composer audit checks for security advisories; composer outdated lists newer package versions. Use audit for safety checks and outdated when planning upgrades—together they guide secure maintenance.

Q: What extra tools should I use with composer audit for stronger PHP dependency security?

A: Use tools like roave/security-advisories to block installs, Snyk or Symfony PHP Security Checker for deeper scans, and Packagist’s advisory API for supplemental feeds and automation.

Q: What common issues occur when running composer audit and how do I fix them?

A: Common issues include outdated Composer, network blocks to Packagist, stale cache, and false positives; fix by composer self-update, ensuring network access, clearing cache, and using –locked or updating constraints.

Q: What are best practices for maintaining secure PHP dependencies with composer audit?

A: Best practices include running composer audit locally and in CI regularly, committing composer.lock, pruning unused deps, replacing abandoned packages quickly, and automating alerts for critical findings.

curtisharmon
Curtis has spent over two decades guiding hunters and anglers through the backcountry of Montana and Wyoming. His expertise in elk hunting and fly fishing has made him a sought-after voice in the outdoor community. Curtis combines traditional woodsmanship with modern techniques to help readers succeed in the field.

Related articles

Recent articles