Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Warden Laravel Package

dgtlss/warden

Warden is a Laravel package that runs security audits against your application, flagging common misconfigurations, exposed files, and risky settings. It provides actionable Artisan reports and configurable checks so teams can catch issues early and keep production environments protected.

View on GitHub
Deep Wiki
Context7

Getting Started

Warden is a static security auditor for Laravel apps: it scans source, Blade views, config and dependency files for sinks, credentials and misconfigurations, then reports findings and exits non-zero when a gate is breached.

composer require --dev 'dgtlss/warden:^2.2' --with-all-dependencies
php artisan vendor:publish --tag=warden-config   # if you want to tune rules/suppressions
php artisan warden:audit

Read in this order:

  1. UPGRADE.md (§ notifications 2.1 → 2.2) — the only intentional behaviour changes in this release.
  2. The published config/warden.php — rules, custom audits, warden.notifications.*.
  3. The README’s notification delivery table — it encodes the flag × channel × incompleteness matrix better than the release notes do.

The first use case to get working is local scan → baseline → gate in CI:

# 1. See what a clean tree looks like today
php artisan warden:audit

# 2. Freeze current findings so new code is judged, not the backlog
php artisan warden:audit --write-baseline

# 3. Fail the build only on new high-severity issues
php artisan warden:audit --fail-on=high

Only after that should you add formats, SARIF upload, or notifications — otherwise you’ll be debugging reporter output and gate semantics at the same time.

Implementation Patterns

One scan, many artifacts. The headline feature of 2.2 is that a single scan produces multiple reports with identical timestamps, suppressions, coverage and gate:

php artisan warden:audit --fail-on=high --format=github \
  --report=json-v2.1:build/warden.json \
  --report=sarif:build/warden.sarif \
  --report=junit:build/warden.xml

Treat --format as “the human/console-facing primary output” and repeated --report as “machine artifacts”. Because the gate is shared, JUnit <failure> elements and GitHub annotations appear iff the scan breaches --fail-on — you never get a green JUnit file next to a red exit code.

CI shape. A pattern that maps cleanly onto the generated workflows:

Artifact Consumer Format
Console humans reading the job log default
GitHub annotations PR diff view --format=github (only inside Actions)
SARIF code scanning tab / opt-in upload --report=sarif:…
JUnit test-report tabs, flaky-history tooling --report=junit:…
JSON dashboards, trend storage, custom gates --report=json-v2.1:…
- run: php artisan warden:audit --fail-on=high --format=github
    --report=json-v2.1:build/warden.json
    --report=sarif:build/warden.sarif
- uses: github/codeql-action/upload-sarif@v3
  if: always()
  with: { sarif_file: build/warden.sarif }

if: always() matters: the upload step must run when the gate fails, which is exactly when SARIF is most interesting. If you regenerate the workflow with Warden’s CI scaffold, note it now preserves existing stages, retains artifacts, and installs npm when a lockfile is present.

Split fast vs. full scans. Run --only=<rules> or a narrow path set on pre-commit, and a full audit nightly. Path arguments normalise (./app, app/. and app are equivalent), so the same exclusions behave the same in both invocations.

Suppressions as reviewable code. Prefer baseline files for volume and explicit suppressions for the long tail:

// config/warden.php
'suppressions' => [
    // rule-wide: legitimately noisy rule, no fingerprint key at all
    ['rule' => 'blade.unescaped-output', 'reason' => 'Legacy marketing partials', 'expires' => '2026-03-01'],

    // single finding: lowercase sha-256 of the fingerprint Warden prints
    ['rule' => 'sql.raw', 'fingerprint' => '9f2c…', 'reason' => 'Revisit with query builder', 'expires' => '2026-01-15'],
],

Always set expires. It turns a permanent “we’ll fix it later” into a dated TODO that resurfaces on its own.

Programmatic reporting. If you embed Warden in a dashboard or an Artisan command of your own, choose the schema deliberately:

new JsonReporter();                 // strict 2.0.0 contract, no gate/coverage fields
new JsonReporter(extended: true);   // 2.1.0 schema with gate + scanned/skipped/error counts

Keep consumers of the default JsonReporter on 2.0.0 semantics — public object serialisation is unchanged, so existing parsers keep working.

Notifications. Delivery is strictly advisory: it never changes the exit code, so gating stays in --fail-on and nowhere else. The warden.notifications.on_errors tri-state is the control you’ll actually touch:

  • missing / null → existing filters apply (incomplete scans with no findings stay silent);
  • true → incomplete scans notify even in finding-only mode;
  • false → incomplete reports silenced entirely, including ones with findings.

Use true on main/nightly where a broken scan is itself an incident, leave it unset on PRs.

Gotchas and Tips

Exit 2 is not exit 1. Malformed suppressions abort before scanning with code 2. A fingerprint with uppercase hex, a 2026-02-30 expiry, or accidental sha256: prefixing all land here. If your CI treats any non-zero as “findings”, you’ll page someone about a typo in a YAML date. Distinguish 2 (config error) from 1 (gate breached).

Duplicate destinations are rejected up front. Repeated --report paths that resolve to the same file — including via symlinks and existing hard links — fail before scanning or writing anything. build/report.json and build/../build/report.json are the same destination. It’s a safety feature, but it bites when a ${{ }} expansion collapses two variables to the same value.

Default JSON has no gate or coverage fields. --format=json (and --report=json:…) emit the strict 2.0.0 schema. If your dashboard needs gate status or scanned/skipped/error counts, ask for json-v2.1 or extended: true. The schema versions (2.0.0, 2.1.0 for reports, 1.0.0 baselines, SARIF 2.1.0, GitLab 15.2.4) are decoupled from the package version — pin parsers to the schema, not to ^2.2.

Incomplete coverage counts are lower bounds. An unreadable directory makes a scan incomplete, and since an unreadable tree may hide any number of files, scanned is a floor, not a total. Two consequences: don’t alert on “scanned dropped by 3%” as a drift signal, and check directory permissions in containerised CI (a bind-mounted vendor path running as nobody is the classic culprit).

Hidden files are now scanned, .env is not. .env.example and .github/** participate in credential scanning, so committed placeholder values that look like real tokens will surface. Every match on a line is examined now — expect more than one finding per line where you previously saw one. .env itself stays excluded; keep it that way.

Suppression semantics changed shape. A rule-wide suppression must omit the fingerprint key entirely — an empty string is a malformed fingerprint and gives you exit 2. Also, fingerprints are lowercase SHA-256; if you generate them yourself from a script, normalise case.

Stricter sanitizer scoping exposes old findings. Sanitizers now apply only to their corresponding sink, and alias/typed-Request/named-argument resolution is improved. Code that was silent under cross-context sanitizers will start reporting. This is the main reason to re-baseline deliberately after upgrading: generate on a branch, diff the baseline file, and confirm each delta is expected rather than blanket-accepting.

Names are stable across refactors, mostly. Existing fingerprints and baselines keep matching unchanged source after name resolution — but renaming a class, file or parameter can still orphan a suppression. Prune suppressions periodically; a no-op suppression is invisible debt.

Inactive custom audits are still valid IDs. A registered-but-disabled custom audit can appear in --only and --skip, and reports as skipped coverage rather than being rejected. Don’t “clean up” those identifiers during refactors — you’ll turn working CI invocations into errors.

Notifications never fail the build. Delivered incomplete reports now describe the error instead of claiming all-clear — good — but nothing about delivery touches the exit code. If someone asks “why didn’t Slack stop the merge?”, the answer is that it never could.

Teams truncates at 28,000 encoded bytes. Scan status and counts survive; details get cut with a pointer back to the full report. If a Teams alert looks stubby, that’s by design — follow the link rather than tuning the message.

Generated CI is a scaffold, not a managed file. Re-running the generator on an existing repo merges into stages and preserves artifacts, but review the diff before committing: npm-shrinkwrap.json takes precedence over package-lock.json, and the report locations tell you which lockfile was picked up, which is the fastest way to confirm the generator guessed correctly.

--format=github needs the Actions environment. Use it only inside a workflow job; outside Actions you get plain output at best and confusing annotations at worst. Gate on the exit code, not on whether annotations appeared.

Debugging a surprising scan: re-run with standard console verbosity (-v) and add --report=json-v2.1:/tmp/w.json to inspect per-file coverage and skipped counts. If skipped is non-zero, check path normalisation and exclusions before suspecting a rule — path strings like ./app and app/. select identically now, so an exclusion that only matched one spelling was already broken.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console