PHPackages                             richardstyles/wire-shield - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Security](/categories/security)
4. /
5. richardstyles/wire-shield

ActiveLibrary[Security](/categories/security)

richardstyles/wire-shield
=========================

Monitors Livewire update requests for deserialization attack patterns (CVE-2025-54068 and related gadget chains).

00[2 PRs](https://github.com/RichardStyles/wire-shield/pulls)PHPCI passing

Since Feb 12Pushed 1mo agoCompare

[ Source](https://github.com/RichardStyles/wire-shield)[ Packagist](https://packagist.org/packages/richardstyles/wire-shield)[ RSS](/packages/richardstyles-wire-shield/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependenciesVersions (3)Used By (0)

Wire Shield
===========

[](#wire-shield)

A Laravel package that monitors Livewire update requests for deserialization attack patterns and other security threats. Built as a response to [CVE-2025-54068](https://github.com/livewire/livewire/security/advisories/GHSA-29cq-5w36-x7w3) and related gadget chain exploits.

Installation
------------

[](#installation)

```
composer require richardstyles/wire-shield
```

The package auto-discovers and registers all middleware into the `web` middleware group. Zero configuration required.

To publish the config file:

```
php artisan vendor:publish --tag=wire-shield-config
```

Requirements
------------

[](#requirements)

- PHP 8.2+
- Laravel 11 or 12
- Livewire 3 or 4

How It Works
------------

[](#how-it-works)

The package registers a pipeline of three middleware that inspect every Livewire POST request (`X-Livewire` header) before it reaches Livewire's own handler. The core scanner performs a single pass over all components, with each scan type independently toggleable via config.

By default the package operates in **monitor mode** — threats are logged and events are dispatched, but requests are allowed through. Set `block_suspicious_requests` to `true` to reject malicious requests with a 403.

Scanning Features
-----------------

[](#scanning-features)

All scanning runs in the `ScanLivewirePayloads` middleware with a single iteration over components. Each feature can be toggled independently.

### Deserialization Detection

[](#deserialization-detection)

**Config:** `enabled` (default: `true`)

The core scanner. Inspects the `updates` and `calls` fields in each Livewire component payload for:

- **Known gadget classes** — Synthetic tuples (`[value, {"s": key, "class": "..."}]`) containing classes from the configurable `dangerous_classes` list. This is the exact CVE-2025-54068 attack pattern.
- **Unexpected classes** — Any synthetic tuple with a `class` key that isn't in the dangerous list is still flagged, since normal Livewire updates never contain class metadata.
- **Unknown synthesizer keys** — Synthetic tuples with unrecognised `s` values. Livewire has a fixed set of synthesizer abbreviations (`str`, `arr`, `mdl`, etc.). Unknown keys may indicate tampering.
- **Dangerous callables** — String values matching known dangerous PHP functions (`system`, `exec`, `passthru`, `eval`, etc.) via a precompiled regex pattern.
- **Suspicious property names** — Update property names that match known reconnaissance patterns observed in the wild as precursors to CVE exploitation. Examples include `areFormStateUpdateHooksDisabledForTesting`, `activeComponent`, and `cachedMountedTableAction` — internal Filament/Livewire properties that should never appear in legitimate update requests. Uses prefix matching so `activeComponent` also catches `activeComponentId`, etc.

### Snapshot Scanning

[](#snapshot-scanning)

**Config:** `scan_snapshots` (default: `true`)

Decodes the JSON `snapshot` string in each component and scans the `data` field using the same detection logic as the core scanner. Provides defence-in-depth against scenarios where an attacker has a compromised `APP_KEY` and can forge valid checksums.

Also detects **malformed snapshots** — snapshot fields that contain invalid JSON.

### Call Method Validation

[](#call-method-validation)

**Config:** `scan_call_methods` (default: `true`)

Validates method names in `components[].calls[].method` against a configurable `dangerous_methods` list. Flags:

- **PHP magic methods** — `__construct`, `__destruct`, `__wakeup`, `__sleep`, `__serialize`, `__unserialize`, `__toString`, `__invoke`, `__clone`, `__debugInfo`, `__set_state`
- **Livewire lifecycle hooks** — `mount`, `boot`, `hydrate`, `dehydrate`, `render`, `updating`, `updated`, `booted`

These methods should never be invoked directly via the wire protocol. Their presence in a call indicates probing or exploitation attempts.

### Magic Method Pattern Detection

[](#magic-method-pattern-detection)

**Config:** `scan_magic_method_patterns` (default: `true`)

Flags ANY method call starting with double underscore (`__`) that isn't in the known `dangerous_methods` list. Catches reconnaissance probes like `__foobar` that attempt to:

- Trigger custom `__call()` magic method handlers
- Discover undocumented internal methods
- Bypass exact-match validation

### Path Traversal Detection

[](#path-traversal-detection)

**Config:** `scan_path_traversal` (default: `true`)

Scans all string values in updates and call parameters for directory traversal patterns:

- **Basic patterns** — `../` and `..\`
- **URL encoded** — `%2e%2e%2f`, `%2e%2e%5c`
- **Double encoded** — `%252e%252e%252f`, `%252e%252e%255c`

Detects attempts to escape the current directory to access unauthorized files or resources.

### Memo Children Monitoring

[](#memo-children-monitoring)

**Config:** `scan_memo_children` (default: `false`)

Livewire excludes `memo.children` from its HMAC checksum calculation, meaning an attacker can tamper with child component IDs without triggering a checksum failure. Inspects children for:

- **Suspiciously long IDs** — Child IDs exceeding 40 characters
- **Namespace separators** — IDs containing backslashes (`\`), suggesting injected class references
- **Excessive count** — More children than the configurable `max_children_count` threshold

Disabled by default as it is a more aggressive check.

Middleware
----------

[](#middleware)

### ValidateLivewirePayloadSize

[](#validatelivewirepayloadsize)

**Config:** `enforce_payload_size` (default: `false`)

Livewire's built-in `max_size` check uses the `Content-Length` header, which can be spoofed with chunked transfer encoding. This middleware measures the actual request body size via `strlen($request->getContent())` and rejects payloads exceeding `max_payload_bytes` (default: 1MB) with a 413 response.

Disabled by default since Livewire already has its own size check.

### ScanLivewirePayloads

[](#scanlivewirepayloads)

**Config:** `enabled` (default: `true`)

The consolidated scanner that performs a single pass over all Livewire components. Runs all scanning features described above (deserialization detection, snapshot scanning, call method validation, memo children monitoring) in one loop. Snapshot JSON is decoded once per component regardless of how many snapshot-based features are enabled.

### ThrottleSuspiciousRequests

[](#throttlesuspiciousrequests)

**Config:** `throttle_offenders` (default: `false`)

Tracks IPs that trigger threats using Laravel's `RateLimiter` and escalates responses:

StrikesLevelResponse1-2`warning`Log only (default behaviour from other middleware)3-5`throttle`429 Too Many Requests with `Retry-After` header6+`block`403 Forbidden for the decay periodStrike counts decay after `offender_decay_minutes` (default: 60 minutes). Thresholds are configurable via `offender_thresholds`.

Disabled by default. Enable for active defence against scanning tools like LivePyre.

Events
------

[](#events)

### LivewireDeserializationAttempt

[](#livewiredeserializationattempt)

Dispatched once per request when any scan detects threats. Contains the consolidated list of all threats found.

**Properties:**

PropertyTypeDescription`ipAddress``string`Client IP address`userAgent``string`Truncated user agent string`requestPath``string`Request path`threats``array`Array of threat details (type, severity, path, detail)`timestamp``string`ISO 8601 timestamp### Custom Logging via Event Listeners

[](#custom-logging-via-event-listeners)

By default, Wire Shield logs threats to your configured log channel. If you prefer full control over logging and alerting, disable built-in logging and listen to the `LivewireDeserializationAttempt` event:

```
// config/wire-shield.php
'log_threats' => false,
'dispatch_events' => true,
```

#### Abstract Listener Classes

[](#abstract-listener-classes)

Wire Shield provides abstract listener classes that handle common patterns:

- **AbstractThreatListener** — Base class with severity filtering, threat analysis, and formatting utilities
- **AbstractNotificationListener** — Extends `AbstractThreatListener` with rate limiting to prevent notification spam

**Available utility methods:**

```
// Severity checks
$this->hasCriticalThreats($event);
$this->hasHighThreats($event);
$this->getHighestSeverity($event); // 'critical', 'high', 'medium', or null
$this->shouldHandle($event, 'high'); // Only handle high+ threats

// Filtering
$this->filterBySeverity($event, 'critical');
$this->filterByType($event, 'known_gadget_class');
$this->getUniqueTypes($event);

// Formatting
$this->formatSummary($event);
$this->buildContext($event);
```

#### Example Implementations

[](#example-implementations)

Copy these complete implementations to your `app/Listeners` directory:

**SecurityLogListener** - Logs to a dedicated security channel:

```
namespace App\Listeners;

use Illuminate\Support\Facades\Log;
use RichardStyles\WireShield\Events\LivewireDeserializationAttempt;
use RichardStyles\WireShield\Listeners\AbstractThreatListener;

class SecurityLogListener extends AbstractThreatListener
{
    public function handle(LivewireDeserializationAttempt $event): void
    {
        if (!$this->shouldHandle($event, 'high')) {
            return;
        }

        $logger = Log::channel('security');

        if ($this->hasCriticalThreats($event)) {
            $logger->critical('Livewire threat detected', $this->buildContext($event));
        } else {
            $logger->warning('Livewire threat detected', $this->buildContext($event));
        }
    }
}
```

**SlackNotificationListener** - Sends formatted Slack messages:

```
namespace App\Listeners;

use Illuminate\Support\Facades\Http;
use RichardStyles\WireShield\Events\LivewireDeserializationAttempt;
use RichardStyles\WireShield\Listeners\AbstractNotificationListener;

class SlackNotificationListener extends AbstractNotificationListener
{
    protected function getMinimumSeverity(): string
    {
        return 'critical';
    }

    protected function shouldRateLimit(): bool
    {
        return true;
    }

    protected function sendNotification(LivewireDeserializationAttempt $event): void
    {
        $webhookUrl = config('services.slack.webhook_url');

        Http::post($webhookUrl, [
            'attachments' => [
                [
                    'color' => $this->getHighestSeverity($event) === 'critical' ? '#ff0000' : '#ff9900',
                    'title' => $this->formatTitle($event),
                    'text' => $this->formatBody($event),
                    'footer' => 'Wire Shield | ' . $event->timestamp,
                ],
            ],
        ]);
    }
}
```

**SiemListener** - Sends to SIEM/monitoring platform:

```
namespace App\Listeners;

use Illuminate\Support\Facades\Http;
use RichardStyles\WireShield\Events\LivewireDeserializationAttempt;
use RichardStyles\WireShield\Listeners\AbstractThreatListener;

class SiemListener extends AbstractThreatListener
{
    public function handle(LivewireDeserializationAttempt $event): void
    {
        // Send all threats to SIEM
        Http::timeout(5)->retry(3)->post(config('services.siem.endpoint'), [
            'event_type' => 'security.livewire.threat',
            'severity' => $this->getHighestSeverity($event),
            'source_ip' => $event->ipAddress,
            'user_agent' => $event->userAgent,
            'request_path' => $event->requestPath,
            'timestamp' => $event->timestamp,
            'threat_count' => count($event->threats),
            'threat_types' => $this->getUniqueTypes($event),
            'threats' => $event->threats,
            'has_critical' => $this->hasCriticalThreats($event),
            'app' => config('app.name'),
            'environment' => config('app.env'),
        ]);
    }
}
```

#### Register Your Listeners

[](#register-your-listeners)

**Laravel 12:** Ensure event discovery is enabled in `bootstrap/app.php`:

```
return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(/* ... */)
    ->withMiddleware(/* ... */)
    ->withEvents(discover: [
        __DIR__.'/../app/Listeners',
    ])
    ->create();
```

Listeners in `app/Listeners` will be automatically discovered and registered.

**Laravel 11:** Add them to `app/Providers/EventServiceProvider.php`:

```
use RichardStyles\WireShield\Events\LivewireDeserializationAttempt;
use App\Listeners\SecurityLogListener;
use App\Listeners\SlackNotificationListener;

protected $listen = [
    LivewireDeserializationAttempt::class => [
        SecurityLogListener::class,
        SlackNotificationListener::class,
    ],
];
```

### RepeatOffenderDetected

[](#repeatoffenderdetected)

Dispatched when an IP crosses an escalation threshold (warning, throttle, or block).

**Properties:**

PropertyTypeDescription`ipAddress``string`Offending IP address`strikeCount``int`Current strike count`escalationLevel``string``warning`, `throttle`, or `block``timestamp``string`ISO 8601 timestampConfiguration
-------------

[](#configuration)

All features are toggleable. Key config options:

```
return [
    'enabled' => true,                          // Master switch
    'block_suspicious_requests' => false,        // 403 on detection
    'log_threats' => true,                       // Built-in logging (disable to use events only)
    'log_channel' => null,                       // Dedicated log channel
    'scan_snapshots' => true,                    // Snapshot data scanning
    'scan_call_methods' => true,                 // Call method validation
    'scan_magic_method_patterns' => true,        // Magic method pattern detection
    'scan_path_traversal' => true,               // Path traversal detection
    'scan_memo_children' => false,               // Memo children checking
    'throttle_offenders' => false,               // IP-based throttling
    'enforce_payload_size' => false,             // Body size enforcement
    'max_payload_bytes' => 1048576,              // 1MB
    'flag_unknown_synthesizer_keys' => true,     // Flag unknown synth keys
    'dispatch_events' => true,                   // Event dispatching
    'dangerous_classes' => [...],                // 21 known gadget chains
    'dangerous_callables' => [...],              // 20 dangerous functions
    'dangerous_methods' => [...],                // Magic + lifecycle methods
    'suspicious_update_properties' => [...],     // Recon probe property names
    'known_synthesizer_keys' => [...],           // Livewire's synth keys
    'offender_decay_minutes' => 60,              // Strike decay period
    'offender_thresholds' => [                   // Escalation thresholds
        'warning' => 1, 'throttle' => 3, 'block' => 6,
    ],
    'max_scan_depth' => 15,                      // Recursion limit
    'max_children_count' => 50,                  // Children count limit
];
```

Threat Types
------------

[](#threat-types)

TypeSeverityScanner`known_gadget_class`criticalDeserialization / Snapshot`unexpected_class_in_update`criticalDeserialization / Snapshot`unknown_synthesizer_key`highDeserialization / Snapshot`dangerous_callable`mediumDeserialization / Snapshot`suspicious_property_update`highDeserialization`malformed_snapshot`highSnapshot`dangerous_method_call`highCall method`suspicious_magic_method_pattern`highCall method`path_traversal_attempt`highDeserialization / Snapshot`suspicious_child_id`high/criticalMemo children`excessive_children_count`mediumMemo childrenArchitecture
------------

[](#architecture)

Internally, threats are represented as typed `Threat` data objects rather than raw arrays:

```
use RichardStyles\WireShield\Data\Threat;
use RichardStyles\WireShield\Enums\ThreatSeverity;
use RichardStyles\WireShield\Enums\ThreatType;

// Created by scanners
new Threat(
    type: ThreatType::KnownGadgetClass,
    severity: ThreatSeverity::Critical,
    componentIndex: 0,
    path: 'components.0.updates.data.name',
    detail: 'Known gadget chain class: GuzzleHttp\Psr7\FnStream',
);
```

Both `ThreatType` and `ThreatSeverity` are backed string enums. `ThreatType` has 9 cases covering all detection patterns, while `ThreatSeverity` has 3 cases: `Critical`, `High`, and `Medium`. The `Threat` DTO is a `final readonly` class with a `toArray()` method used at the serialisation boundary — events and log context always receive plain arrays for external consumers.

### Middleware Pipeline

[](#middleware-pipeline)

The ServiceProvider registers three middleware in this order:

1. `ValidateLivewirePayloadSize` — Cheapest rejection (body size check)
2. `ScanLivewirePayloads` — Consolidated scanner (single pass over all components)
3. `ThrottleSuspiciousRequests` — Reads accumulated threats, must run last

### Performance

[](#performance)

The consolidated scanner is optimised for minimal overhead on legitimate requests:

- **Single component iteration** — all scan types run in one loop
- **Snapshot decoded once** — shared between snapshot scanning and memo children monitoring
- **O(1) lookups** — dangerous classes, synthesizer keys, and method names use hash maps via `array_flip`
- **Precompiled regex** — dangerous callable detection uses a single `preg_match` instead of iterating
- **Lazy config caching** — config values and maps are resolved once per request

Testing &amp; Quality
---------------------

[](#testing--quality)

```
# Run tests (83 tests)
vendor/bin/pest

# Static analysis (level 8)
vendor/bin/phpstan analyse

# Code formatting
vendor/bin/pint
```

All source files use `declare(strict_types=1)`. PHPStan is configured at level 8 with Larastan.

License
-------

[](#license)

MIT

###  Health Score

20

—

LowBetter than 14% of packages

Maintenance60

Regular maintenance activity

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity15

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/72f95111dc92cab50312c2d2917c40ec8fd45afb3bb437bd2a5755ca56126dcb?d=identicon)[RichardStyles](/maintainers/RichardStyles)

---

Top Contributors

[![RichardStyles](https://avatars.githubusercontent.com/u/1642215?v=4)](https://github.com/RichardStyles "RichardStyles (5 commits)")

### Embed Badge

![Health badge](/badges/richardstyles-wire-shield/health.svg)

```
[![Health](https://phpackages.com/badges/richardstyles-wire-shield/health.svg)](https://phpackages.com/packages/richardstyles-wire-shield)
```

###  Alternatives

[defuse/php-encryption

Secure PHP Encryption Library

3.9k162.4M214](/packages/defuse-php-encryption)[roave/security-advisories

Prevents installation of composer packages with known security vulnerabilities: no API, simply require it

2.9k97.3M6.4k](/packages/roave-security-advisories)[mews/purifier

Laravel 5/6/7/8/9/10 HtmlPurifier Package

2.0k16.7M113](/packages/mews-purifier)[robrichards/xmlseclibs

A PHP library for XML Security

41278.1M118](/packages/robrichards-xmlseclibs)[bjeavons/zxcvbn-php

Realistic password strength estimation PHP library based on Zxcvbn JS

86917.5M63](/packages/bjeavons-zxcvbn-php)[illuminate/encryption

The Illuminate Encryption package.

9229.7M280](/packages/illuminate-encryption)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
