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

Firewall Symfony Laravel Package

kanopi/firewall-symfony

Symfony integration for Kanopi Firewall that adds configurable request firewalls and access rules, letting you allow or deny traffic by IP, network, or custom criteria. Protect routes and enforce access policies directly from Symfony configuration.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

What this release is. kanopi/firewall-symfony v1.3.0 is a security-control bundle (WAF-style rule engine + ALTCHA challenge page) wrapping the kanopi/firewall library. It is a Symfony bundle, not a Laravel package. That distinction is the single most important fact in this assessment and should be confirmed before any effort is committed.

Architecture fit.

  • The functionality (path/method/ASN/IP/rate rules, challenge pages, block/lockdown/redirect responses, a kanopi:firewall:check CLI gate) is squarely relevant to our stack: edge-adjacent request filtering, bot/facet-crawl mitigation, and a CI-assertable access policy.
  • However, the bundle integrates through Symfony's bundle registration, DI container and event system. Laravel consumes Symfony components but does not boot bundles. Native composer require into a Laravel app will not wire rules, listeners, or the challenge controller.
  • Realistic options: (a) depend on kanopi/firewall directly and write a thin Laravel service provider + middleware adapter ourselves; (b) run it only in a Symfony-fronted service (e.g. a gateway/edge app); (c) vendor the library's rule evaluation into existing middleware. Option (a) is the only path that gets the value inside Laravel, and it is our code, not theirs.
  • The release notes confirm the bundle "runs the library in mode: exception" and writes all responses itself — meaning the parts we would reuse in Laravel are the library, and the bundle is mostly response-writing and console wiring. That reduces the reuse surface but also reduces coupling risk: we would be depending on the more portable layer.

Technical risk (ranked).

  1. Breaking behavioural change for CI — highest. kanopi:firewall:check previously exited 2 ("challenged") when configuration was missing or unreadable. It now exits 64. Any pipeline gate asserting "URL is challenged" would have passed on a totally unconfigured app. That is a real security blind spot that this release fixes, but it also means pipelines written against the old codes will now fail (correctly). Audit every CI job that consumes this exit code before upgrading; treat 0/1/2/3/64/70 as a contract and pin it in our test fixtures.
  2. Rule semantics change silently via the dependency floor bump (^2.33 → ^2.36). Several fixes make rules that never fired start firing:
    • ASN rules (asn:16509) previously never matched — any ASN rule in our config now begins blocking traffic for the first time.
    • Path normalisation now applies, so //wp-json/… and /%77p-json/… evasions are caught. Expect new blocks on previously-passing traffic, including legitimate double-slash URLs emitted by some clients/proxies.
    • Rate-limit keys containing a capital letter now count the named field instead of sharing a single global counter. Throttles that appeared "not to work" will now engage; conversely, per-field counters may change effective limits. Each of these is a production traffic-shape change disguised as a patch-level library bump. They need a staged rollout with logging/dry-run, not a same-day deploy.
  3. Dependency range breadth and transitive drift. ^2.36 will happily float to 2.4x/2.5x, and this release is direct evidence that library patch releases change rule behaviour. A ^ floor on a security control is an unpriced maintenance liability; we should lock and review, not float.
  4. CDN/edge header change. Every bundle-written response now sends private, no-store, no-cache, must-revalidate, max-age=0, Pragma, Expires: 0, Surrogate-Control: no-store, CDN-Cache-Control: no-store. Correct for the bug, but it is a behavioural change for any downstream cache (Varnish, Fastly, Cloudflare, nginx proxy_cache) and may interact with existing surrogate-key/purge logic. Also note the fix depends on the CDN honouring these headers — this is mitigation, not enforcement.
  5. Platform constraint narrowness. PHP 8.1–8.5 and Symfony 6.4 / 7.4 / 8.1 are specific branch pins, not open ranges. In a Laravel app the symfony/* component versions are transitively pinned by the framework; a direct requirement on this bundle can force a Laravel major bump or a conflict. This must be resolved on paper before any install attempt.
  6. Solo-maintainer / vendor concentration. Small vendor, security-critical surface, no prior assessment on file. There is no stated support policy, CVE process, or backport commitment. We would be adopting an unhedged dependency at the request path.
  7. query_count (new rule variable) is a capability, not a risk, but it implies config work to cap facet crawling — scope it as a separate, optional item.

Key questions to answer before approval.

  • Are we actually targeting Laravel, or is a Symfony service in scope? If Laravel, who owns the adapter, and what is its maintenance cost?
  • Which CDN/edge do we run, and does it honour CDN-Cache-Control / Surrogate-Control the way Fastly/Pantheon does? Have we reproduced the #18 loop, or is this a fix for a bug we never had?
  • What do we currently rely on for this layer (Cloudflare WAF, nginx rules, Laravel throttling)? Is this additive or a replacement? Duplication creates two sources of truth for "who is blocked."
  • Do any of our existing rules use ASN, capitalised rate-limit fields, or rules we believe "never fire"? Those are exactly the ones changing behaviour.
  • What is our policy on ^ version constraints for request-path security dependencies?
  • Who is paged when the challenge page misbehaves, and what is the kill switch (mode: exception off / rule disable) and its tested runbook?

Integration Approach

Stack fit — the honest read. As-is: not a drop-in fit for Laravel. Fit for a Symfony service: strong. The value we can actually bank in Laravel is the library layer (kanopi/firewall ^2.36) plus our own middleware; the bundle's own fixes (response header set, doctor warning, CLI exit codes, challenge notices) are only directly available if we run the bundle. This should be stated to stakeholders plainly rather than discovered mid-sprint.

Compatibility gates (do these first, in order).

  1. Confirm the runtime target: Laravel version, PHP version, resolved symfony/* component versions. Compare against PHP 8.1–8.5 / Symfony 6.4·7.4·8.1. Any mismatch is a stop condition, not a workaround.
  2. Run composer why-not kanopi/firewall-symfony ^1.3 in a throwaway branch of the real app. Treat the output as the go/no-go artefact.
  3. Decide adapter vs. Symfony-service deployment and record the decision.

Migration path.

  • Phase 0 — Inventory (0.5 day). Enumerate every existing rule, CI job, and cache layer touching this control. Grep CI for exit-code handling of kanopi:firewall:check; list ASN rules, capitalised rate-limit keys, and any rule with a path that could be normalised differently.
  • Phase 1 — Dry-run in staging (1–2 days). Upgrade to v1.3.0 with rules in log-only mode. Diff blocked/challenged decisions against current behaviour for 24–72h of replayed or mirrored traffic. The library behavioural fixes mean the diff is the deliverable, not the deploy.
  • Phase 2 — CI correction (0.5 day). Update gates to the full code set (0 allowed, 1 blocked, 2 challenged, 3 redirected, 64 unreadable config, 70 internal error). Add a regression test asserting exit 64 on an empty config — this is the #21 blind spot and the cheapest thing we get from this release.
  • Phase 3 — Header verification (0.5 day). Confirm the full header set is emitted on block, lockdown, challenge, redirect, solved-challenge responses, and on the blocked_response: http_exception error page. Verify cache behaviour at the edge with a real challenged URL under load (the #18 repro: two concurrent visitors, one challenged URL).
  • Phase 4 — Production rollout (staged). Canary one pool, watch block rate and challenge solve rate; roll forward only if block-rate delta is within the pre-agreed envelope.
  • Phase 5 — Optional capability. Adopt query_count rules for facet crawling as a separate, individually reversible change.

Sequencing rationale. Dry-run before CI changes (so a rules regression isn't masked by a gate that now behaves differently). CI changes before production (so the gate is trustworthy when you need it). Header verification before production because the header set is the actual fix, and it is only correct if the CDN honours it.

Rollback. Composer-pinned previous version (^1.2), rules held in config not code where possible, and a documented "disable rule" path. Confirm the downgrade does not re-expose the #18 cache loop in a way that would force us to stay forward-only during an incident.

Effort estimate. ~3–5 engineer-days for the Symfony-native path (upgrade, dry-run, CI, verification). Add 5–10 days if a Laravel adapter must be written and maintained — and that estimate should be owned by the team that will carry it, not by this assessment.

Operational Impact

Maintenance burden. Low code surface, non-trivial policy surface. The package is a security control at the request path; it will require an owner, an upgrade cadence, and a review process for rule changes. This release demonstrates why: a library patch moved ASN matching, path normalisation and rate-limit keying without any change on our side. Budget a recurring review — monthly dependency review, quarterly rule audit — not a one-off install.

Version policy (recommendation). Pin the library exactly or with a tight ~ range and upgrade deliberately with dry-run diffs. Floating ^2.36 on request-path security logic is not an acceptable risk posture and should be written into the assessment outcome, not left as a team preference.

Support and vendor risk. Single-vendor, small-team package. We should (a) confirm we can read and patch the library if needed (MIT-style licence, small codebase — verify), (b) document our own runbook rather than depending on upstream docs, and (c) keep an independent outer layer (edge WAF/rate limiting) so a package defect is degraded, not catastrophic. The #18 bug is the case study: an upstream header omission silently converted a security challenge into a denial-of-service loop for authenticated-looking traffic, and only a library patch release fixed it.

Scaling characteristics. Rule evaluation is per-request CPU work in the application tier — cheap but not free, and it is the tier you least want to add work to under load. Challenge pages shift load to the edge/browser but, as #18 showed, only if the CDN behaves. Two specific scaling notes: the fixed header set disables caching on every firewall-written response (fine, but do not let someone "optimise" it back), and per-field rate-limit counters now mean counters multiply with the number of keyed fields — size any shared counter store accordingly.

Failure modes to design for.

  • Cache poisoning of the challenge page (#18) — mitigated here but CDN-dependent; monitor challenge solve-failure rate as a leading indicator.
  • Over-blocking after the dependency bump — ASN rules and normalised paths firing for the first time; needs alerting on block-rate change, not just error rate.
  • CI gate false-pass (#21) — the old exit-2 behaviour meant a green pipeline on an unconfigured app. Fixed, but only if we update the gate; if we don't, we keep the blind spot.
  • Misconfiguration via global.path_source: script_name — the new doctor warning exists because this silently prefixes every path with the subdirectory in a non-WordPress app. Run the doctor step in CI, not just locally.
  • Dependency conflict at deploy time — a narrow Symfony/PHP window means an unrelated framework upgrade can become blocked by this package. Track it as a constraint on the platform roadmap.
  • Open redirect via solved challenge — fixed in 2.35.0; confirms the package belongs on the security-review list, not the "utility" list.

Ramp-up. For a Symfony team: half a day to a day — config is declarative, the CLI (kanopi:firewall:check) is self-documenting once the exit codes are documented, and the doctor command covers the main footgun. For a Laravel team writing an adapter: 3–5 days of pairing, plus a written internal doc, because the upstream README assumes Symfony wiring throughout and will not answer the questions that actually come up. Two things to teach explicitly: (1) the exit-code contract, because it is a CI gate people will otherwise misread; (2) the response-header set, because it is load-bearing and looks like boilerplate someone will want to trim.

Recommendation. Adopt on the Symfony path, with the CI exit-code update and a staged dry-run as non-negotiable prerequisites. On the Laravel path, treat this as evaluate-and-probably-adapt, not adopt: the library is worth the dependency, the bundle is not usable without our own middleware, and that middleware needs a named owner before approval. In both cases, pin the version, monitor block rate and challenge solve rate as first-class signals, and keep an independent outer control so this package is a defence in depth rather than the only lock on the door.

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.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels