Skip to content

Commit 2d21955

Browse files
committed
Dispatch event
1 parent 7906d6f commit 2d21955

File tree

6 files changed

+85
-11
lines changed

6 files changed

+85
-11
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,11 @@ By default, the middleware transforms malicious input to `null`. You may configu
7777

7878
### Terminate request
7979

80-
Instead of transforming malicious input, you may configure the middleware to terminate the request whenever anything malicious has been found. You may do this by setting the `middleware.terminate_request_on_malicious_input` to `true`, which will throw an HttpException with status code 403.
80+
Instead of transforming malicious input, you may configure the middleware to terminate the request whenever anything malicious has been found. You may do this by setting the `middleware.terminate_request_on_malicious_input` to `true`, which will throw an `HttpException` with status code 403.
81+
82+
### Dispatch event
83+
84+
You may configure the middleware to dispatch an event whenever malicious input has been found. Setting the `middleware.dispatch_event_on_malicious_input` to `true` will dispatch an `ProtoneMedia\LaravelXssProtection\Events\MaliciousInputFound` event with the malicious keys and full request.
8185

8286
## Changelog
8387

config/xss-protection.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,7 @@
1515
'completely_replace_malicious_input' => true,
1616

1717
'terminate_request_on_malicious_input' => false,
18+
19+
'dispatch_event_on_malicious_input' => false,
1820
],
1921
];

src/Events/MaliciousInputFound.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
namespace ProtoneMedia\LaravelXssProtection\Events;
4+
5+
use Illuminate\Http\Request;
6+
7+
class MaliciousInputFound
8+
{
9+
public function __construct(public array $keys, public Request $request)
10+
{
11+
}
12+
}

src/Events/Sanitized.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace ProtoneMedia\LaravelXssProtection\Cleaners;
4+
5+
class RequestSanitized
6+
{
7+
}

src/Middleware/XssCleanInput.php

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use GrahamCampbell\SecurityCore\Security;
77
use Illuminate\Foundation\Http\Middleware\TransformsRequest;
88
use ProtoneMedia\LaravelXssProtection\Cleaners\BladeEchoes;
9+
use ProtoneMedia\LaravelXssProtection\Events\MaliciousInputFound;
910
use Symfony\Component\HttpFoundation\File\UploadedFile;
1011

1112
class XssCleanInput extends TransformsRequest
@@ -40,6 +41,13 @@ class XssCleanInput extends TransformsRequest
4041
//
4142
];
4243

44+
/**
45+
* Array of sanitized keys.
46+
*
47+
* @var array
48+
*/
49+
protected $sanitizedKeys = [];
50+
4351
/**
4452
* Create a new instance.
4553
*
@@ -63,13 +71,29 @@ public function __construct(Security $security, BladeEchoes $bladeEchoCleaner)
6371
*/
6472
public function handle($request, Closure $next)
6573
{
74+
$this->sanitizedKeys = [];
75+
6676
foreach (static::$skipCallbacks as $callback) {
6777
if ($callback($request)) {
6878
return $next($request);
6979
}
7080
}
7181

72-
return parent::handle($request, $next);
82+
$this->clean($request);
83+
84+
if (count($this->sanitizedKeys) === 0) {
85+
return $next($request);
86+
}
87+
88+
if ($this->enabledInConfig('dispatch_event_on_malicious_input')) {
89+
event(new MaliciousInputFound($this->sanitizedKeys, $request));
90+
}
91+
92+
if ($this->enabledInConfig('terminate_request_on_malicious_input')) {
93+
abort(403, 'Malicious input found.');
94+
}
95+
96+
return $next($request);
7397
}
7498

7599
/**
@@ -90,28 +114,33 @@ protected function transform($key, $value)
90114
}
91115

92116
if ($value instanceof UploadedFile) {
93-
return config('xss-protection.middleware.allow_file_uploads') ? $value : null;
117+
if ($this->enabledInConfig('allow_file_uploads')) {
118+
return $value;
119+
}
120+
121+
$this->sanitizedKeys[] = $key;
122+
123+
return null;
94124
}
95125

96126
$output = $this->security->clean((string) $value);
97127

98-
if (!config('xss-protection.middleware.allow_blade_echoes')) {
128+
if (!$this->enabledInConfig('allow_blade_echoes')) {
99129
$output = $this->bladeEchoCleaner->clean((string) $output);
100130
}
101131

102132
if ($output === $value) {
103133
return $output;
104134
}
105135

106-
if (config('xss-protection.middleware.terminate_request_on_malicious_input')) {
107-
abort(403, 'Malicious input found.');
108-
}
136+
$this->sanitizedKeys[] = $key;
109137

110-
if (config('xss-protection.middleware.completely_replace_malicious_input')) {
111-
return null;
112-
}
138+
return $this->enabledInConfig('completely_replace_malicious_input') ? null : $output;
139+
}
113140

114-
return $output;
141+
private function enabledInConfig($key): bool
142+
{
143+
return config("xss-protection.middleware.{$key}");
115144
}
116145

117146
/**

tests/MiddlewareTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
use Illuminate\Http\Request;
44
use Illuminate\Http\UploadedFile;
5+
use Illuminate\Support\Facades\Event;
6+
use ProtoneMedia\LaravelXssProtection\Events\MaliciousInputFound;
57
use ProtoneMedia\LaravelXssProtection\Middleware\XssCleanInput;
68
use Symfony\Component\HttpKernel\Exception\HttpException;
79

@@ -19,6 +21,24 @@
1921
expect($request->input('key'))->toBe('test');
2022
});
2123

24+
it('can dispatch an event with the transformed keys and request', function () {
25+
$request = Request::createFromGlobals()->merge([
26+
'key' => 'test<script>script</script>',
27+
]);
28+
29+
config(['xss-protection.middleware.dispatch_event_on_malicious_input' => true]);
30+
31+
Event::fake();
32+
33+
/** @var XssCleanInput $middleware */
34+
$middleware = app(XssCleanInput::class);
35+
$middleware->handle($request, fn ($request) => $request);
36+
37+
Event::assertDispatched(function (MaliciousInputFound $event) use ($request) {
38+
return $event->request === $request && $event->keys === ['key'];
39+
});
40+
});
41+
2242
it('can add a callback to skip a request', function () {
2343
class SkipXssCleanInput extends XssCleanInput
2444
{

0 commit comments

Comments
 (0)