As developers, we trust our dependencies a lot.
In a Laravel project, I can run:
composer installand suddenly my project has dozens or even hundreds of packages.
On the frontend, it can be even more.
npm installand our application gets a large dependency tree.
Most of the time this is exactly what we want.
We don't want to build a logging library, HTTP client, date library, authentication package or frontend utility from zero.
But there is one thing we sometimes forget:
Our application is not only our code.
It is also the code we depend on.
This is why software supply-chain security is becoming a much bigger topic in modern development. OWASP's 2025 Top 10 has expanded this area into A03: Software Supply Chain Failures, covering not only vulnerable libraries but also compromised dependencies, build systems, CI/CD, package repositories and the broader software delivery process.
We usually think only about our own code
When I review an application, the first thing I normally think about is:
Is my PHP code secure?
Is my Laravel code secure?
Are my SQL queries safe?
Are my APIs protected?
These are important questions.
But there is another question:
What exactly am I installing into my application?
Suppose I have:
My Laravel Application
|
├── Laravel
├── Payment Package
├── Excel Package
├── PDF Package
├── HTTP Client
├── Logging Package
└── 50+ Other DependenciesEven if my own code is perfect, a compromised package can become a problem.
That is the part many developers don't think about.
The dependency tree is much bigger than it looks
When I run:
composer require some/packageI may think:
"I installed one package."
Technically, that is often not the complete story.
That package can depend on other packages.
Those packages can depend on more packages.
So we have:
My Application
↓
Package A
↓
Package B
↓
Package C
↓
Package DPackage D may not be something I have ever directly chosen.
But it is still part of my application.
OWASP specifically points out that developers need to track both direct and transitive dependencies because an untracked dependency can remain outside normal security monitoring.
This is why:
"I don't use that package directly"
does not automatically mean:
"That package cannot affect my application."
Composer is not just a convenience tool
As a Laravel developer, Composer is part of my daily life.
We use it for:
- Laravel
- Symfony components
- Guzzle
- Monolog
- PHPUnit
- SDKs
- Payment gateways
- Excel libraries
- PDF libraries
- Authentication packages
- Internal packages
Composer makes development much faster.
But every package we install increases the software supply chain we need to trust.
That doesn't mean we should stop using open-source packages.
That would be unrealistic.
It means we should become more aware of what we are actually installing.
npm makes the problem even more interesting
Frontend dependency trees can become very large.
One package can bring multiple dependencies.
And those dependencies can bring more dependencies.
For a Laravel application using React, Vue or another frontend framework, we may have both:
PHP / Composer dependenciesand:
JavaScript / npm dependenciesSo our application can have two completely different dependency ecosystems.
This means dependency security should not be treated as only a PHP problem.
It is an application-wide problem.
A vulnerable package is not the only problem
This is something I think is very important.
When people hear "dependency security", they usually think:
"There is a CVE in an old package."
That is only one part.
Modern software supply-chain risks can also include:
- A malicious package
- A compromised maintainer account
- A dependency being taken over
- Dependency confusion
- Typosquatting
- Compromised build systems
- Malicious CI/CD changes
- Compromised plugins
- Untrusted package sources
- Tampered artifacts
- Stolen deployment credentials
OWASP's software supply-chain guidance specifically includes source-code threats, build-environment threats, dependency threats and deployment/runtime threats.
So the problem is much larger than:
"Run an update once a month."
Imagine a package suddenly becomes malicious
Let's imagine I install:
composer require example/packageEverything works.
After six months, somebody gains access to the package's publishing process.
A new version is published.
My CI pipeline automatically installs the newest compatible version.
Now the malicious code may enter my build without me changing my application code.
This is the scary part.
From the Git commit history, I may see:
No application code changed.But the software that gets deployed has changed.
That is why dependency updates should be treated as software changes.
"Latest version" is not always the safest strategy
I have seen developers do:
composer updateand:
npm updatewithout checking what is going to change.
Sometimes it works.
Sometimes it creates unexpected breaking changes.
From a security point of view, blindly updating everything is also not a great strategy.
We should know:
- What changed
- Which package changed
- Why it changed
- Whether the package is maintained
- Whether the new version fixes a vulnerability
- Whether it introduces breaking changes
- Whether the build is reproducible
OWASP recommends deliberate dependency management, monitoring for vulnerable or unmaintained components and applying updates according to risk rather than treating patching as a blind periodic task.
What about composer.lock?
This file is extremely important.
For an application, I prefer having the exact resolved dependency versions recorded in:
composer.lockThis gives the team a reproducible dependency state.
The same idea applies to:
package-lock.jsonor the equivalent lock file used by the frontend package manager.
The difference between:
"Install whatever version is currently available"and:
"Install the exact versions we have tested"is very important for production systems.
But lock files are not a security guarantee by themselves.
A locked malicious package is still a malicious package.
The lock file gives us reproducibility.
It does not automatically give us trust.
CI/CD is part of the supply chain too
This is an area developers sometimes forget.
We protect:
Production serverbut don't pay enough attention to:
Git repository
CI/CD
Build server
Package registry
Deployment credentials
Artifact storageBut the CI/CD pipeline is effectively a path into production.
For example:
GitHub
↓
CI/CD
↓
composer install
↓
npm install
↓
Build
↓
DeployIf the pipeline is compromised, an attacker may not need to attack the production server directly.
They can attack the process that builds the application.
OWASP explicitly notes that CI/CD environments can have weaker security than the systems they build and deploy, making pipeline protection an important part of supply-chain security.
Never keep production secrets casually available in CI
This is another lesson I take seriously.
A deployment pipeline may have access to:
AWS credentials
Database passwords
Application secrets
SSH keys
API keys
Payment gateway credentialsIf the pipeline or repository account is compromised, these secrets can become more dangerous than the vulnerable dependency itself.
So CI/CD needs:
- Least privilege
- Protected environments
- Strong authentication
- Secret management
- Restricted deployment permissions
- Branch protection
- Audit logs
The goal should be:
Compromising one part of the development environment should not automatically give access to everything.
Should we stop using open source?
Absolutely not.
Open source is one of the biggest reasons modern software development is so fast.
Laravel itself is part of the open-source ecosystem.
Composer packages allow us to build products much faster.
Frontend packages allow small teams to build applications that previously required much larger teams.
The answer is not:
"Don't use dependencies."
The answer is:
Know your dependencies.
I would start with a dependency inventory
For every production application, I want to know:
What packages are installed?
Which versions are installed?
Which packages are direct dependencies?
Which are transitive dependencies?
Which are no longer maintained?
Which have known vulnerabilities?
Which package registry do they come from?
Who can change the dependency?
How is the dependency introduced into production?This is basically the idea behind a Software Bill of Materials, or SBOM.
OWASP recommends maintaining visibility into the software components and dependencies used by an application and supports automated SBOM and dependency monitoring approaches.
Dependency scanning should become normal
I don't think security scanning should happen only before a big release.
It should become part of CI.
For example:
Pull Request
↓
Tests
↓
Dependency Security Check
↓
Build
↓
DeployIf a serious dependency vulnerability appears, the team should know about it quickly.
This is much better than discovering six months later that an important package has been vulnerable for a long time.
OWASP recommends continuous monitoring of dependency versions and known vulnerabilities and mentions tools such as Dependency-Track and Dependency-Check as examples of automated approaches.
Remove dependencies you don't need
This sounds simple, but it is very useful.
Every dependency has:
- Maintenance cost
- Upgrade cost
- Security risk
- Compatibility risk
- Operational risk
Sometimes we install a package for one small feature.
Six months later the feature is removed.
But the package remains.
Now we have a dependency that provides no business value.
I prefer periodically asking:
"Why is this package still here?"
If there is no good answer, remove it.
Reducing dependencies also reduces the attack surface. OWASP explicitly recommends removing unused dependencies and unnecessary components.
Don't copy random code into your project
There is another supply-chain problem that is easy to miss.
Developers sometimes copy code from:
- GitHub
- Stack Overflow
- Blogs
- Gists
- Forums
- Random repositories
Then that code becomes part of the application.
Now it may not appear in:
composer.jsonor:
package.jsonSo normal dependency tracking may not even know it exists.
OWASP's open-source software risk guidance specifically calls out copied or vendored code as one reason dependencies can remain untracked.
I am not saying:
"Never copy code."
I am saying:
Know what you are copying.
Don't trust package names blindly
Typosquatting is another simple example.
Suppose the package we want is:
awesome-libraryAn attacker may publish:
awesome-librayOne small typing mistake.
If someone installs it without checking, the malicious package can enter the project.
This is why package source and package identity matter.
OWASP recommends obtaining components from trusted sources and, where appropriate, verifying package provenance and signatures.
The development laptop is also part of the supply chain
We sometimes think:
"My production server is secure."
But what about the developer laptop?
What about:
- IDE extensions
- Git credentials
- SSH keys
- Composer credentials
- npm credentials
- Cloud CLI credentials
- Browser sessions
- Local environment variables
OWASP's guidance explicitly includes developer workstations, IDEs, repositories, CI/CD systems and artifact repositories within the software supply-chain security picture.
This means security starts before the code reaches Git.
My practical approach for a Laravel project
For a Laravel application, I would keep the process simple.
First:
composer validateThen inspect the dependency tree and outdated packages.
Regularly check:
composer auditFor JavaScript:
npm auditBut I would not blindly fix everything returned by these commands.
I would review:
- Severity
- Affected package
- Whether the package is actually used
- Whether the vulnerable code path is relevant
- Available fixed versions
- Compatibility impact
- Whether the dependency can be removed
Security tooling gives us information.
Developers still have to make the decision.
One more thing: deployment should be reproducible
I don't like the idea of production being built from:
whatever packages happen to be available todayI prefer:
Known source
+
Known dependency versions
+
Known build process
+
Known artifact
=
Production deploymentThat makes debugging easier.
It also makes security incidents easier to investigate.
If something changes unexpectedly, we have a better chance of knowing where it came from.
The biggest lesson
As developers, we like to say:
"I wrote this application."
But technically, modern applications are assembled from thousands of pieces.
Our code.
Open-source libraries.
Frameworks.
Operating systems.
Build tools.
CI/CD systems.
Cloud services.
Package registries.
Third-party APIs.
That is our real software supply chain.
So application security cannot stop at our controller code.
We have to secure the path from:
Developer → Git → Dependencies → Build → Artifact → Deployment → Production
A vulnerability in our own code is a problem.
A compromised dependency can be a much bigger problem because we may trust it automatically.
That is why I think dependency management should not be treated as housekeeping.
It is part of application security.
And for anyone building Laravel, PHP, React or SaaS products, this is becoming something we cannot ignore anymore.