Skip to content

Commit 1f169da

Browse files
authored
Prevents spam by blocking links posing as mentions (#949)
* add invalid mention rule * Fix code styling
1 parent 791d4f4 commit 1f169da

File tree

7 files changed

+119
-3
lines changed

7 files changed

+119
-3
lines changed

app/Http/Requests/CreateReplyRequest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@
66
use App\Models\Thread;
77
use App\Models\User;
88
use App\Rules\HttpImageRule;
9+
use App\Rules\InvalidMentionRule;
910

1011
class CreateReplyRequest extends Request
1112
{
1213
public function rules()
1314
{
1415
return [
15-
'body' => ['required', new HttpImageRule()],
16+
'body' => ['required', new HttpImageRule(), new InvalidMentionRule()],
1617
'replyable_id' => 'required',
1718
'replyable_type' => 'required|in:'.Thread::TABLE,
1819
];

app/Http/Requests/ThreadRequest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44

55
use App\Rules\DoesNotContainUrlRule;
66
use App\Rules\HttpImageRule;
7+
use App\Rules\InvalidMentionRule;
78

89
class ThreadRequest extends Request
910
{
1011
public function rules()
1112
{
1213
return [
1314
'subject' => ['required', 'max:60', new DoesNotContainUrlRule()],
14-
'body' => ['required', new HttpImageRule()],
15+
'body' => ['required', new HttpImageRule(), new InvalidMentionRule()],
1516
'tags' => 'array',
1617
'tags.*' => 'exists:tags,id',
1718
];

app/Http/Requests/UpdateReplyRequest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
namespace App\Http\Requests;
44

55
use App\Rules\HttpImageRule;
6+
use App\Rules\InvalidMentionRule;
67

78
class UpdateReplyRequest extends Request
89
{
910
public function rules()
1011
{
1112
return [
12-
'body' => ['required', new HttpImageRule()],
13+
'body' => ['required', new HttpImageRule(), new InvalidMentionRule()],
1314
];
1415
}
1516

app/Rules/InvalidMentionRule.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace App\Rules;
4+
5+
use Illuminate\Contracts\Validation\Rule;
6+
7+
/**
8+
* This rule validates links are not diguised as mentions.
9+
*/
10+
final class InvalidMentionRule implements Rule
11+
{
12+
public function passes($attribute, $value): bool
13+
{
14+
return ! preg_match('/\[@.*\]\(http.*\)/', $value);
15+
}
16+
17+
public function message(): string
18+
{
19+
return 'The :attribute field contains an invalid mention.';
20+
}
21+
}

tests/Feature/ForumTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,3 +326,16 @@
326326

327327
Notification::assertNothingSent();
328328
});
329+
330+
test('cannot fake a mention', function () {
331+
$this->login();
332+
333+
$response = $this->post('/forum/create-thread', [
334+
'subject' => 'How to work with Eloquent?',
335+
'body' => 'Hey [@joedixon](https://somethingnasty.com)',
336+
'tags' => [],
337+
]);
338+
339+
$response->assertSessionHas('error', 'Something went wrong. Please review the fields below.');
340+
$response->assertSessionHasErrors(['body' => 'The body field contains an invalid mention.']);
341+
});

tests/Feature/ReplyTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use App\Models\Thread;
77
use App\Models\User;
88
use App\Notifications\MentionNotification;
9+
use App\Rules\InvalidMentionRule;
910
use Illuminate\Foundation\Testing\DatabaseMigrations;
1011
use Illuminate\Notifications\DatabaseNotification;
1112
use Illuminate\Support\Facades\Notification;
@@ -217,3 +218,30 @@
217218

218219
Notification::assertNothingSent();
219220
});
221+
222+
test('cannot fake a mention when creating a reply', function () {
223+
$thread = Thread::factory()->create(['subject' => 'The first thread', 'slug' => 'the-first-thread']);
224+
225+
$this->login();
226+
227+
$response = $this->post('/replies', [
228+
'body' => 'Hey [@joedixon](https://somethingnasty.com)',
229+
'replyable_id' => $thread->id,
230+
'replyable_type' => Thread::TABLE,
231+
]);
232+
233+
$response->assertSessionHas('error', 'Something went wrong. Please review the fields below.');
234+
$response->assertSessionHasErrors(['body' => 'The body field contains an invalid mention.']);
235+
});
236+
237+
test('users cannot edit a reply with a fake mention', function () {
238+
$user = $this->createUser();
239+
$thread = Thread::factory()->create(['slug' => 'the-first-thread']);
240+
$reply = Reply::factory()->create(['author_id' => $user->id(), 'replyable_id' => $thread->id()]);
241+
242+
$this->actingAs($user);
243+
244+
Livewire::test(EditReply::class, ['reply' => $reply])
245+
->call('updateReply', 'Hey [@joedixon](https://somethingnasty.com)')
246+
->assertHasErrors(['body' => InvalidMentionRule::class]);
247+
});
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
use App\Rules\InvalidMentionRule;
4+
5+
it('passes when no invalid mentions are detected', function ($body) {
6+
expect((new InvalidMentionRule())->passes('body', $body))->toBeTrue();
7+
})->with([
8+
'Hello, I\'m looking for some help',
9+
'I\'ve seen [this link](https://example.com), is it legit?',
10+
"### Help needed!
11+
\n
12+
Hello @joedixon I am hoping you can help.
13+
\n
14+
Here is some **bold** and _italic_ text
15+
\n
16+
> I'm quoting you now!
17+
\n
18+
`code goes here`
19+
\n
20+
```javascript
21+
const string = 'more code goes here'
22+
```
23+
\n
24+
[link](https://example.com)
25+
\n
26+
![image](https://example.com/image.png)",
27+
]);
28+
29+
it('fails when invalid mentions are detected', function ($body) {
30+
expect((new InvalidMentionRule())->passes('body', $body))->toBeFalse();
31+
})->with([
32+
'[@driesvints](https://somethingnasty.com)',
33+
'Hey [@joedixon](https://somethingnasty.com), is it legit?',
34+
"### Help needed!
35+
\n
36+
Hello [@joedixon](https://somethingnasty.com) I am hoping you can help.
37+
\n
38+
Here is some **bold** and _italic_ text
39+
\n
40+
> I'm quoting you now!
41+
\n
42+
`code goes here`
43+
\n
44+
```javascript
45+
const string = 'more code goes here'
46+
```
47+
\n
48+
[link](https://example.com)
49+
\n
50+
![image](https://example.com/image.png)",
51+
]);

0 commit comments

Comments
 (0)