Snyk’s PHP Composer scanner can identify vulnerable dependencies in your project, but it doesn’t magically fix them for you.
You’ve got a PHP project, and you’re using Composer to manage your dependencies. You ran snyk test (or snyk monitor, snyk wizard, etc.) and it spit out a list of vulnerabilities in your composer.lock file. Now what? The scanner itself doesn’t change your code or your composer.lock file. It’s just a reporter. The real work is in understanding why a package is vulnerable and then deciding how to mitigate it.
Here’s how to dig into those Snyk reports and actually fix the issues:
1. Understand the Vulnerability Report
Snyk reports vulnerabilities with a severity level (Low, Medium, High, Critical), a CVE (Common Vulnerabilities and Exposures) identifier, and a description. The most crucial piece of information for fixing is the vulnerable version range and the fixed version.
For example, you might see something like:
High severity vulnerability in symfony/symfony found in versions < 4.4.32, >= 5.0.0 < 5.4.10, >= 5.5.0 < 5.5.17.
Introduced by: twig/twig ^3.0
Remediation: Upgrade twig/twig to version 3.3.4 or higher.
This tells you:
- What’s vulnerable: A component within the
symfony/symfonypackage. - The problematic versions: Any
symfony/symfonyversion before 4.4.32, or between 5.0.0 and 5.4.10 (exclusive of 5.4.10), or between 5.5.0 and 5.5.17 (exclusive of 5.5.17). This is often because the vulnerability exists in a transitive dependency – something another one of your dependencies relies on. In this example,twig/twigis the transitive dependency that pulls in the vulnerablesymfony/symfonycomponent. - How to fix it: Upgrade
twig/twigto at least version3.3.4.
2. Identify the Direct Dependency Causing the Vulnerability
Snyk reports often point to transitive dependencies. This means a package you directly require (require in your composer.json) depends on another package that has the vulnerability. You need to find which of your direct dependencies is pulling in the vulnerable package.
Diagnosis:
Run composer why-not <vulnerable-package-name> <vulnerable-version> (e.g., composer why-not twig/twig 3.3.3). This command will tell you which of your direct dependencies is preventing you from upgrading the specified package to a non-vulnerable version.
Example:
If Snyk reports a vulnerability in twig/twig and suggests upgrading to 3.3.4, but you can’t directly upgrade twig/twig because another package requires an older version, you’d run:
composer why-not twig/twig 3.3.4
This will output something like:
twig/twig 3.3.3 requires some/other-package ^1.0
some/other-package 1.0.0 requires twig/twig ^3.0
This tells you that some/other-package is the direct dependency that’s locking you into an older version of twig/twig.
3. Upgrade the Direct Dependency
Once you know which of your direct dependencies is the culprit, your primary goal is to upgrade that dependency. Often, the newer version of your direct dependency will have updated its own dependencies to include the fix.
Diagnosis:
Check the composer.json file for the direct dependency identified in step 2. Look for its latest stable version.
Fix:
Modify your composer.json to require a newer version of the direct dependency. For example, if some/other-package was the issue:
In composer.json:
{
"require": {
"some/other-package": "^1.2" // Updated to a version that supports twig/twig 3.3.4+
}
}
Then run composer update some/other-package.
Why it works: By updating some/other-package, you’re telling Composer to find a version of some/other-package that satisfies its own requirements (which will likely include a newer version of twig/twig that doesn’t have the vulnerability). Composer will then resolve all dependencies, including twig/twig, to compatible versions.
4. Direct Package Upgrade (If Step 3 Fails or Isn’t Applicable)
Sometimes, the direct dependency you need to upgrade is a core part of your application or a framework component, and there isn’t a newer version of another package forcing the old version. In this case, you might need to directly upgrade the vulnerable package itself, if Snyk indicates a fixed version is available.
Diagnosis:
Check the composer.json for the vulnerable package identified by Snyk. See if a newer version exists that is not in the vulnerable range.
Fix:
Directly update the vulnerable package in your composer.json:
In composer.json:
{
"require": {
"twig/twig": "^3.3.4" // Directly updated to the fixed version
}
}
Then run composer update twig/twig.
Why it works: You are explicitly telling Composer to use a version of the package that is known to be free of the reported vulnerability. Composer will then ensure all other dependencies are compatible with this new version.
5. Use Composer’s --with-all-dependencies Flag (Carefully)
If you have a complex dependency tree and are struggling to resolve versions manually, Composer’s --with-all-dependencies flag can sometimes help. This flag tells Composer to try and update all dependencies to their latest possible versions that satisfy the constraints.
Diagnosis:
After attempting manual upgrades and running composer update, if you still have unresolved vulnerabilities or dependency conflicts, this might be your next step.
Fix:
Run composer update --with-all-dependencies.
Why it works: This is a more aggressive update strategy. Composer will attempt to find the highest possible versions for all your dependencies, which often includes updating transitive dependencies to versions that have had their own vulnerabilities patched. Caution: This can sometimes introduce breaking changes if dependencies have incompatible API changes between versions. Always run your test suite after using this flag.
6. Pinning to a Specific Patch Version
If a vulnerability is in a very old, unmaintained package or a specific patch version is crucial, you might need to explicitly "pin" your dependency to that exact version.
Diagnosis:
Snyk reports a vulnerability in a package vulnerable/package and states it’s fixed in 1.2.3. You find that 1.2.3 is not in the vulnerable range.
Fix:
In composer.json:
{
"require": {
"vulnerable/package": "1.2.3"
}
}
Then run composer update vulnerable/package.
Why it works: By specifying an exact version (e.g., "1.2.3") instead of a version range (e.g., "^1.0"), you force Composer to use precisely that version, bypassing any newer, potentially vulnerable versions or older, incompatible versions.
7. Consider Alternative Packages or Removing Dependencies
In extreme cases, if a dependency is unmaintained, has no security fixes, or is too difficult to upgrade, you might need to replace it with an alternative package or remove the functionality entirely.
Diagnosis: Snyk reports a critical vulnerability in a package that is no longer maintained by its author, and there’s no clear upgrade path or replacement suggested by the community.
Fix:
- Find an alternative: Search for actively maintained packages that provide similar functionality. Update your
composer.jsonto require the new package and refactor your code to use its API. - Remove: If the functionality isn’t critical, remove the dependency and the related code.
Why it works: This eliminates the vulnerable code from your project entirely, thereby removing the attack vector.
After you’ve applied these fixes and run composer update, run snyk test again. You should see the reported vulnerabilities disappear. The next error you’ll encounter is likely a dependency conflict that arises from trying to satisfy multiple, incompatible version constraints.