jorijn/laravel-security-checker
Laravel Security Checker scans your Composer dependencies for known vulnerabilities using the FriendsOfPHP security advisories database. Run it via an Artisan command or schedule it, and get notified through email, Slack, Telegram, or Discord when security issues affect your app.
jorijn/laravel-security-checker compares your composer.lock against the FriendsOfPHP security advisories database and reports known vulnerabilities, with optional notifications to mail, Slack, Discord, Telegram, Teams and ntfy.
composer require jorijn/laravel-security-checker
php artisan vendor:publish --tag=config # publishes config/security-checker.php
php artisan security-checker:check
Look at these first, in order:
config/security-checker.php — the only file you normally touch..env — every config key has an LCS_* env counterpart; keep the truth there so it's per-environment.The first use case worth wiring up is a scheduled daily run that posts to a team channel, because the command is only useful if someone actually sees the output. A second, equally common use case is a CI step: the command exits non-zero when vulnerabilities are found, so a plain step in the pipeline is enough to fail the build.
Scheduled monitoring (the default pattern). Register the command in routes/console.php or the scheduler and let notifications do the delivery:
Schedule::command('security-checker:check')
->dailyAt('07:00')
->withoutOverlapping();
Notifications are configured entirely in config/security-checker.php; enable one channel per environment and keep the webhook URLs / recipients in .env. Don't fan out to every channel — a weekly digest in one place beats a daily message nobody reads.
CI gate. Run php artisan security-checker:check as a pipeline step early (before the slow test suite) so a vulnerable dependency fails fast. In a production build pipeline where composer install --no-dev is used, add LCS_EXCLUDE_DEV=true so dev-only packages don't block a release. Keep a separate, non-exclude_dev run for the branch that developers actually build against.
The allow_list triage loop. When an advisory is reported that you have decided not to act on, add it to LCS_ALLOW_LIST by CVE identifier rather than by title:
LCS_ALLOW_LIST='["CVE-2024-1234"]'
Prefer CVEs — advisory titles get reworded and then your suppression silently stops matching (or worse, matches the wrong advisory). Pair each entry with a ticket reference in your own notes, and review the list on a schedule; it has no expiry mechanism of its own.
exclude_dev as a signal, not a silencer. Dev dependencies are still recorded in composer.lock even when --no-dev is used at install time, so the checker reports them by default. Set LCS_EXCLUDE_DEV=true only where those packages genuinely never run — production images and release jobs. Leave it off in the environment developers use.
Webhook / on-demand runs. For setups where CI cannot shell into the app (or where you want a single shared check server), the package supports running as a listener that other systems can trigger. That's the right choice for multi-repo organisations; for a single Laravel app, the scheduled command is simpler and easier to debug.
.env quoting rule is not optional. LCS_ALLOW_LIST must be a single-quoted JSON array: LCS_ALLOW_LIST='["CVE-2024-1234","Some title"]'. Double-quoted values get mangled by dotenv's own parsing, and you'll spend an hour wondering why nothing is being suppressed.php artisan tinker → config('security-checker.allow_list')) instead of trusting the run to be quiet.php artisan config:cache will bite you. Any change to .env needs php artisan config:clear (or a re-cache) before the scheduled run sees it — especially in deployments where config is cached at build time.composer.lock — abandoned packages with no advisories, unmaintained transitive dependencies and application-level code are invisible to it. Don't let a green scheduled job substitute for composer outdated and periodic dependency review.|| true the step to keep the pipeline green — use allow_list for accepted risk so that the reason a vulnerability is tolerated is recorded in version control rather than in a CI config.exclude_dev and allow_list are additive and default to off/empty, so v3.0.0 config files keep working untouched. If you publish a new config file, diff it against yours rather than overwriting — the notification blocks are easy to lose.How can I help you explore Laravel packages today?