# A Free, Open Source Laravel Security Scanner You Run With Artisan

> stackshield/scanner checks your booted Laravel app for debug mode, exposed Telescope and Horizon, weak session cookies, CSRF exemptions, missing rate limits and vulnerable dependencies. One composer require, one artisan command, no account needed.

**Author:** Matt King | **Published:** October 13, 2026 | **Category:** Product

---

Most Laravel security incidents we see are not clever. Debug mode left on after a config change. Telescope reachable because a middleware refactor dropped the gate. A CSRF exemption added for one webhook that quietly covers half the API. Each one is a single line of configuration, and none of them shows up in tests.

We have released a free, open source scanner that checks for exactly these, from inside your application:

```bash
composer require --dev stackshield/scanner
php artisan stackshield:scan
```

It supports Laravel 10, 11, 12 and 13 on PHP 8.2 or later, needs no account, and the source is on [GitHub](https://github.com/stack-shield/scanner).

---

## What it checks

The scanner boots your application and inspects what Laravel actually resolved, not what the files on disk suggest. The current checks:

- **Debug mode**: `APP_DEBUG` and `APP_ENV`, judged against the environment the scan runs in
- **Session cookies**: `HttpOnly`, `Secure` and `SameSite` flags, plus the session driver and encryption
- **Security headers** as your application actually emits them
- **Telescope, Horizon and Ignition**: installed, enabled, and whether they are gated
- **Debugbar and Clockwork** left installed or enabled
- **CSRF**: the middleware is in place, and what the exemption list covers
- **Cookie encryption** middleware
- **CORS**: wildcard origins combined with credentials
- **APP_KEY**: set, and not a known default
- **HTTPS posture** in production: secure cookies and `APP_URL`
- **Rate limiting** on login, registration and password reset routes
- **End of life**: framework and PHP versions past security support
- **Dependency advisories**: `composer.lock` checked against Packagist's advisory data

Because it reads the booted app, it can answer questions a file scan cannot. A Telescope gate defined in a service provider, middleware applied through a route group, or an exemption added in `bootstrap/app.php` are all resolved the way Laravel resolves them.

---

## Environment-aware results

A scan on your laptop will find `APP_DEBUG=true`, and that is fine. Every result records the environment it was evaluated in, and severity follows from it: debug mode is critical in production or staging and informational in local. The report states the environment at the top so nobody misreads a local run.

To check what production will actually run with, run the scanner where production environment variables are composed, which for most teams is the deploy pipeline.

---

## Output formats and exit codes

```bash
php artisan stackshield:scan                     # readable report
php artisan stackshield:scan --format=json       # for scripts
php artisan stackshield:scan --format=sarif --output=stackshield.sarif
php artisan stackshield:scan --offline           # skip the dependency audit
php artisan stackshield:scan --fail-on=high      # stricter CI gate
```

The command exits non-zero when it finds anything at or above the `fail_on` severity (critical by default), so it works as a CI gate with no extra scripting. SARIF output contains failures and warnings only, so a clean run produces an empty report instead of annotating every pull request with checks that passed.

---

## Running it in GitHub Actions

The published action runs the scan, uploads SARIF to the repository's Security tab and fails the build at your chosen threshold:

```yaml
name: security
on: [push, pull_request]

permissions:
  contents: read
  security-events: write

jobs:
  stackshield:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with: { php-version: '8.3' }
      - run: composer install --no-interaction --prefer-dist
      - uses: stack-shield/scanner@v1
        with:
          fail-on: high
```

Findings then show up in the Security tab and as annotations on the pull request that introduced them. For other CI systems, run the artisan command directly and use its exit code.

For more on where security checks belong in a pipeline, see [Securing Your Laravel CI/CD Pipeline](/blog/securing-laravel-ci-cd-pipeline-devsecops).

---

## What it cannot see

The scanner is the inside view. It sees your configuration, not your infrastructure. It cannot tell you whether port 3306 is open to the internet, whether your TLS setup is weak at the load balancer, whether a subdomain points at a deleted cloud resource, or whether a proxy is stripping the headers your application sets.

Those are the outside view, and the two can disagree. Your app may set `Strict-Transport-Security` while your CDN drops it, or `APP_DEBUG` may be false in the pipeline but true on the server because someone edited `.env` by hand. That gap is what [StackShield](/free-scan) checks from the outside, against the deployed application.

It is also not a static analyser. For SQL injection patterns, unescaped Blade output and mass assignment, use Larastan, Psalm's taint analysis or semgrep alongside it. [roave/security-advisories](https://github.com/Roave/SecurityAdvisories) is a good companion too: it stops known-vulnerable packages from being installed at all.

---

## Optional: send results to StackShield

Nothing is sent anywhere by default. If you use StackShield, you can report inside-view results to your dashboard and trigger an outside scan of a deployed URL in one step:

```bash
STACKSHIELD_TOKEN=sk_... php artisan stackshield:scan --url=https://staging.example.com --report
```

The payload is the list of check results: slug, status, severity and a short summary. No source code, no environment variables, no secrets. `--no-report` always wins.

---

## Try it

```bash
composer require --dev stackshield/scanner
php artisan stackshield:scan
```

If it finds something, the report includes how to fix it. If you want to see the outside view of the same application, run a [free external scan](/free-scan): it takes under a minute and needs no signup.

---

## Frequently Asked Questions

### Is the StackShield Laravel scanner free?

Yes. stackshield/scanner is open source and free to use. Install it with composer require --dev stackshield/scanner and run php artisan stackshield:scan. It needs no StackShield account and sends nothing to StackShield unless you give it a token and ask it to report.

### Which Laravel versions does it support?

Laravel 10, 11, 12 and 13, on PHP 8.2 or later. It recognises the CSRF middleware and bootstrap-level exemptions used by Laravel 11 and later as well as the older Kernel-based setup.

### Is it a static analysis tool?

No. It inspects the booted application: resolved configuration, registered middleware, routes and installed packages. That is how it can tell whether Telescope is actually gated or whether a CSRF exemption covers a real route. For code-level patterns such as SQL injection or unsafe Blade output, pair it with Larastan, Psalm taint analysis or semgrep.

### Will it flag APP_DEBUG=true on my laptop?

Not as a problem. Every result records the environment it was evaluated in and severity depends on it: debug mode is critical in production or staging and informational in local. To check production settings, run it in your deploy pipeline where production environment variables are set.

### Does it make network calls?

Only one by default: the dependency audit compares composer.lock against advisories from Packagist. Use --offline to skip it. Nothing goes to StackShield unless you set STACKSHIELD_TOKEN and pass --report.

