The API Guys
Automated security scanning pipeline with shield and code review icons on a dark background
·7 min read·The API Guys

The Case for Automated Security Scanning in Your CI/CD Pipeline

SecurityDevOpsLaravelCI/CDPHPReact

Every modern web application has dependencies. A typical Laravel project pulls in dozens of Composer packages. A React frontend built with Next.js can easily have hundreds of npm modules in its dependency tree. Each one of those packages is a potential entry point for an attacker if it contains a known vulnerability and you have not patched it.

The uncomfortable truth is that most security vulnerabilities which reach production were already publicly documented before the code was deployed. They were sitting in dependency trees, hiding in outdated packages, or present in code patterns that an automated tool would have flagged in seconds. The problem is rarely that a fix did not exist. The problem is that nobody checked.

This is where automated security scanning in your CI/CD pipeline comes in. Done well, it turns your build process into a security gate that catches issues before they ever reach a live server.

Why manual checks are not enough

Running composer audit or pnpm audit manually is better than nothing, but it relies on someone remembering to do it. In a busy sprint, with deadlines pressing and features to ship, manual security checks are the first thing that gets skipped. We have seen it happen repeatedly across teams of all sizes.

Automated scanning removes the human variable. If it is part of your pipeline, it runs every time. No exceptions, no forgotten steps, no assumptions that someone else already checked.

The three layers of pipeline security

For the Laravel and React projects we build and maintain, we run three distinct layers of automated security scanning. Each catches a different category of problem, and together they provide comprehensive coverage.

Layer one: dependency auditing

Dependency auditing checks your installed packages against databases of known vulnerabilities. For PHP projects, composer audit does this natively. For JavaScript projects, pnpm audit (or npm audit if you are using npm) performs the same function. These commands can be added to your CI pipeline in a single line and configured to fail the build if critical or high-severity vulnerabilities are detected.

The basic audit commands are a starting point, but tools like Dependabot and Snyk take this significantly further. Dependabot, built into GitHub, monitors your composer.json, package.json, and lock files continuously. When a vulnerability is disclosed in a package you depend on, Dependabot automatically raises a pull request to update to the patched version. It supports both Composer for PHP and npm, pnpm, and Yarn for JavaScript.

Snyk offers similar functionality but with broader scope. It integrates with GitHub, GitLab, and Bitbucket, supports a wide range of CI/CD platforms including GitHub Actions, Jenkins, CircleCI, and Azure Pipelines, and provides detailed vulnerability reports with remediation advice. Snyk also scans container images if you are deploying with Docker, which adds another valuable layer of coverage.

A practical Dependabot configuration for a Laravel and Next.js project might look like this:

version: 2
updates:
  - package-ecosystem: "composer"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
  - package-ecosystem: "npm"
    directory: "/frontend"
    schedule:
      interval: "weekly"
      day: "monday"

This checks both your PHP and JavaScript dependencies weekly and raises pull requests for any available updates. For security-specific updates, Dependabot raises them immediately regardless of the schedule.

Layer two: static analysis

Dependency auditing catches known vulnerabilities in third-party packages, but it does not examine your own code. Static analysis fills that gap by analysing your source code without executing it, looking for type errors, unsafe patterns, potential injection vectors, and architectural violations.

For PHP and Laravel projects, PHPStan is the tool we rely on. Combined with Larastan, it understands Laravel-specific patterns like Eloquent relationships, facades, and container bindings. PHPStan operates on a level system from 0 to 10, allowing you to gradually increase strictness as your codebase matures. Even at a moderate level, it catches issues that would otherwise only surface at runtime, including type mismatches, null pointer errors, and method calls on incorrect types.

Psalm is another excellent option, particularly notable for its taint analysis feature. Taint analysis tracks the flow of user input through your application and flags cases where unsanitised data reaches dangerous operations such as SQL queries, shell commands, or file system calls. This is precisely the kind of vulnerability that leads to injection attacks, and catching it at the static analysis stage is far preferable to discovering it in a penetration test or, worse, in a production incident.

For JavaScript and TypeScript codebases, ESLint with security-focused plugins performs a similar role. The eslint-plugin-security package flags common patterns that can lead to vulnerabilities, while TypeScript's own type system provides a baseline level of safety that plain JavaScript lacks.

Adding PHPStan to a GitHub Actions workflow is straightforward:

- name: Run PHPStan
  run: vendor/bin/phpstan analyse --memory-limit=1G

If PHPStan detects issues at or above your configured level, the build fails. No vulnerable code reaches the main branch.

Layer three: secret detection

The third layer addresses a different but equally critical risk: accidentally committing secrets to your repository. API keys, database credentials, encryption keys, and environment files have no business being in version control, but they end up there with alarming regularity.

As we covered in our post on 600+ Laravel apps exposed to RCE via leaked APP_KEYs, the consequences of a committed secret can be severe. In that case, exposed APP_KEY values enabled full remote code execution on hundreds of Laravel applications.

Tools like GitGuardian, TruffleHog, and gitleaks scan your commits for patterns that look like secrets. GitHub also offers built-in secret scanning for repositories, which checks for known credential formats from major service providers. Integrating any of these into your CI pipeline means that a build will fail if someone accidentally includes a .env file or hardcodes a database password.

This is particularly important in team environments where multiple developers are contributing to the same repository. It only takes one mistake by one developer on one commit, and secret detection catches it before it reaches the remote.

Making it enforceable

Adding security tools to your pipeline is only half the job. The other half is ensuring that failures actually block deployments. A security scan that runs but whose results are ignored is no better than having no scan at all.

In practice, this means configuring your pipeline so that security scan failures are treated as build failures. Merge requests should not be mergeable if the security checks have not passed. Branch protection rules in GitHub, GitLab, and Bitbucket all support this, and it is one of the most impactful settings you can enable.

It also means being disciplined about not creating exceptions. The moment someone merges a pull request with a known critical vulnerability because the deadline is tight, the entire system loses its value. If your team needs to make a risk-based decision to ship with a known low-severity issue, that is reasonable. But it should be a documented, deliberate choice rather than a default behaviour.

The cost of adding security scanning

One common objection to pipeline security scanning is that it slows down builds. In our experience, this is overstated. Running composer audit takes seconds. pnpm audit is similarly fast. PHPStan analysis on a medium-sized Laravel project typically completes in under a minute. Secret scanning with gitleaks is measured in seconds.

Even if your total pipeline time increases by two or three minutes, that is a negligible cost compared to what you gain. As we discussed in The Hidden Cost of Ignoring Security Updates, the cost of a security breach runs into incident response, forensic investigation, regulatory fines under GDPR, customer communication, and reputational damage. A few extra minutes of CI time is a trivial insurance premium.

Where this fits in your broader security practice

Automated pipeline scanning is not a replacement for other security practices. It does not eliminate the need for a proper patching schedule, for keeping your framework and language versions current, or for secure coding practices in your team. It is one layer in a defence-in-depth approach.

What it does exceptionally well is catch the easy wins. The known vulnerability in a transitive dependency. The accidentally committed credential. The type confusion that could lead to an injection. These are the issues that automated tools excel at finding, and catching them at the pipeline stage means they never reach production.

If your CI/CD pipeline does not currently include security scanning, start with the basics. Add composer audit and pnpm audit as build steps. Enable Dependabot on your GitHub repositories. Install PHPStan with Larastan and run it on every build. These three steps take less than an hour to set up and will catch the majority of common issues from day one.

From there, you can layer in more advanced tooling as your security practice matures. Snyk for deeper vulnerability analysis. Psalm for taint tracking. gitleaks for comprehensive secret detection. Each addition strengthens the gate between your development environment and production.

The best time to add security scanning to your pipeline was when you first set it up. The second best time is today.

Ready to Start Your Project?

Get in touch with our Leeds-based team to discuss your Laravel or API development needs.