Skip to content

Commit b863354

Browse files
authored
fix: address CodeRabbit review comments and fix PCRE JIT stack overflow
## Summary - Add `high` severity tier to Spanish, French, and German severity maps - Sync 39 missing words from severity maps into profanities lists - Fix byte-to-character offset conversion in all 3 drivers - Refactor severity-before-masking in all 3 drivers - Add path traversal sanitization to Dictionary - Rename `--verbose` to `--detail` in TestCommand (Symfony conflict) - Fix facade assertion no-ops (now throw RuntimeException) - Fix Profanity rule composability with `__callStatic` - Use `mb_strtolower` for false positives in PatternDriver - Stabilize dedup sort with length tiebreaker - Replace `str_word_count` with multibyte-safe `preg_split` - Add UTF-8 guard to PatternDriver - Use original text in PhoneticDriver MatchedWord - Eliminate nested regex quantifiers to prevent PCRE JIT stack overflow in PHP-FPM - Document separator limit in README
1 parent db8457c commit b863354

95 files changed

Lines changed: 5378 additions & 4506 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 436 additions & 267 deletions
Large diffs are not rendered by default.

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
"extra": {
4646
"laravel": {
4747
"providers": [
48-
"Blaspsoft\\Blasp\\ServiceProvider"
48+
"Blaspsoft\\Blasp\\BlaspServiceProvider"
4949
],
5050
"aliases": {
5151
"Blasp": "Blaspsoft\\Blasp\\Facades\\Blasp"

config/blasp.php

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
<?php
2+
3+
return [
4+
5+
/*
6+
|--------------------------------------------------------------------------
7+
| Default Driver
8+
|--------------------------------------------------------------------------
9+
|
10+
| The default detection driver. 'regex' provides full obfuscation
11+
| detection. 'pattern' is faster but only matches exact words.
12+
|
13+
*/
14+
'default' => env('BLASP_DRIVER', 'regex'),
15+
16+
/*
17+
|--------------------------------------------------------------------------
18+
| Default Language
19+
|--------------------------------------------------------------------------
20+
|
21+
| The default language to use for profanity detection.
22+
|
23+
*/
24+
'language' => env('BLASP_LANGUAGE', 'english'),
25+
26+
// Backward compat alias
27+
'default_language' => env('BLASP_LANGUAGE', 'english'),
28+
29+
/*
30+
|--------------------------------------------------------------------------
31+
| Mask Character
32+
|--------------------------------------------------------------------------
33+
|
34+
| The character used to mask detected profanities.
35+
|
36+
*/
37+
'mask' => '*',
38+
39+
// Backward compat alias
40+
'mask_character' => '*',
41+
42+
/*
43+
|--------------------------------------------------------------------------
44+
| Minimum Severity
45+
|--------------------------------------------------------------------------
46+
|
47+
| The minimum severity level to detect. Words below this severity
48+
| will be ignored. Options: mild, moderate, high, extreme
49+
|
50+
*/
51+
'severity' => 'mild',
52+
53+
/*
54+
|--------------------------------------------------------------------------
55+
| Events
56+
|--------------------------------------------------------------------------
57+
|
58+
| When enabled, ProfanityDetected events will be fired automatically
59+
| when profanity is found during a check.
60+
|
61+
*/
62+
'events' => false,
63+
64+
/*
65+
|--------------------------------------------------------------------------
66+
| Cache Configuration
67+
|--------------------------------------------------------------------------
68+
*/
69+
'cache' => [
70+
'enabled' => true,
71+
'driver' => env('BLASP_CACHE_DRIVER'),
72+
'ttl' => 86400,
73+
'results' => true,
74+
],
75+
76+
// Backward compat alias
77+
'cache_driver' => env('BLASP_CACHE_DRIVER'),
78+
79+
/*
80+
|--------------------------------------------------------------------------
81+
| Middleware Configuration
82+
|--------------------------------------------------------------------------
83+
*/
84+
'middleware' => [
85+
'action' => 'reject',
86+
'fields' => ['*'],
87+
'except' => ['password', 'email', '_token'],
88+
'severity' => 'mild',
89+
],
90+
91+
/*
92+
|--------------------------------------------------------------------------
93+
| Model Configuration
94+
|--------------------------------------------------------------------------
95+
|
96+
| Controls how the Blaspable trait behaves on Eloquent models.
97+
| 'sanitize' replaces profanity with the mask character.
98+
| 'reject' throws a ProfanityRejectedException instead of saving.
99+
|
100+
*/
101+
'model' => [
102+
'mode' => env('BLASP_MODEL_MODE', 'sanitize'),
103+
],
104+
105+
/*
106+
|--------------------------------------------------------------------------
107+
| Driver-Specific Configuration
108+
|--------------------------------------------------------------------------
109+
*/
110+
'drivers' => [
111+
'pipeline' => [
112+
'drivers' => ['regex', 'phonetic'],
113+
],
114+
115+
'phonetic' => [
116+
'phonemes' => 4, // metaphone code length (2-8, lower=more aggressive)
117+
'min_word_length' => 3, // skip words shorter than this
118+
'max_distance_ratio' => 0.6, // levenshtein threshold (0.3-0.8, lower=stricter)
119+
'supported_languages' => ['english'],
120+
'false_positives' => [
121+
'fork', 'forked', 'forking',
122+
'beach', 'beaches',
123+
'witch', 'witches',
124+
'sheet', 'sheets',
125+
'deck', 'decks',
126+
'count', 'counts', 'counter', 'county',
127+
'ship', 'shipped', 'shipping',
128+
'duck', 'ducked', 'ducking',
129+
'fudge', 'fudging',
130+
'buck', 'bucks',
131+
'puck', 'pucks',
132+
'bass',
133+
'mass',
134+
'pass', 'passed',
135+
'heck',
136+
'shoot', 'shot',
137+
'what', 'white', 'while', 'whole',
138+
],
139+
],
140+
],
141+
142+
/*
143+
|--------------------------------------------------------------------------
144+
| Character Separators
145+
|--------------------------------------------------------------------------
146+
*/
147+
'separators' => [
148+
'@', '#', '%', '&', '_', ';', "'", '"', ',', '~', '`', '|',
149+
'!', '$', '^', '*', '(', ')', '-', '+', '=', '{', '}',
150+
'[', ']', ':', '<', '>', '?', '.', '/',
151+
],
152+
153+
/*
154+
|--------------------------------------------------------------------------
155+
| Character Substitutions
156+
|--------------------------------------------------------------------------
157+
*/
158+
'substitutions' => [
159+
'/a/' => ['a', '4', '@', 'Á', 'á', 'À', 'Â', 'à', 'Â', 'â', 'Ä', 'ä', 'Ã', 'ã', 'Å', 'å', 'æ', 'Æ', 'α', 'Δ', 'Λ', 'λ'],
160+
'/b/' => ['b', '8', '\\', '3', 'ß', 'Β', 'β'],
161+
'/c/' => ['c', 'Ç', 'ç', 'ć', 'Ć', 'č', 'Č', '¢', '', '<', '(', '{', '©'],
162+
'/d/' => ['d', '\\', ')', 'Þ', 'þ', 'Ð', 'ð'],
163+
'/e/' => ['e', '3', '', 'È', 'è', 'É', 'é', 'Ê', 'ê', 'ë', 'Ë', 'ē', 'Ē', 'ė', 'Ė', 'ę', 'Ę', ''],
164+
'/f/' => ['f', 'ƒ'],
165+
'/g/' => ['g', '6', '9'],
166+
'/h/' => ['h', 'Η'],
167+
'/i/' => ['i', '!', '|', ']', '[', '1', '', 'Ì', 'Í', 'Î', 'Ï', 'ì', 'í', 'î', 'ï', 'ī', 'Ī', 'į', 'Į'],
168+
'/j/' => ['j'],
169+
'/k/' => ['k', 'Κ', 'κ'],
170+
'/l/' => ['l', '!', '|', ']', '[', '£', '', 'Ì', 'Í', 'Î', 'Ï', 'ł', 'Ł'],
171+
'/m/' => ['m'],
172+
'/n/' => ['n', 'η', 'Ν', 'Π', 'ñ', 'Ñ', 'ń', 'Ń'],
173+
'/o/' => ['o', '0', 'Ο', 'ο', 'Φ', '¤', '°', 'ø', 'ô', 'Ô', 'ö', 'Ö', 'ò', 'Ò', 'ó', 'Ó', 'œ', 'Œ', 'ø', 'Ø', 'ō', 'Ō', 'õ', 'Õ'],
174+
'/p/' => ['p', 'ρ', 'Ρ', '', 'þ'],
175+
'/q/' => ['q'],
176+
'/r/' => ['r', '®'],
177+
'/s/' => ['s', '5', '\$', '§', 'ß', 'Ś', 'ś', 'Š', 'š'],
178+
'/t/' => ['t', 'Τ', 'τ'],
179+
'/u/' => ['u', 'υ', 'µ', 'û', 'ü', 'ù', 'ú', 'ū', 'Û', 'Ü', 'Ù', 'Ú', 'Ū', '@', '*'],
180+
'/v/' => ['v', 'υ', 'ν'],
181+
'/w/' => ['w', 'ω', 'ψ', 'Ψ'],
182+
'/x/' => ['x', 'Χ', 'χ'],
183+
'/y/' => ['y', '¥', 'γ', 'ÿ', 'ý', 'Ÿ', 'Ý'],
184+
'/z/' => ['z', 'Ζ', 'ž', 'Ž', 'ź', 'Ź', 'ż', 'Ż'],
185+
],
186+
187+
/*
188+
|--------------------------------------------------------------------------
189+
| False Positives
190+
|--------------------------------------------------------------------------
191+
*/
192+
'false_positives' => [
193+
'hello', 'scunthorpe', 'cockburn', 'penistone', 'lightwater',
194+
'assume', 'bass', 'class', 'compass', 'pass',
195+
'dickinson', 'middlesex', 'cockerel', 'butterscotch', 'blackcock',
196+
'countryside', 'arsenal', 'flick', 'flicker', 'analyst',
197+
'cocktail', 'musicals hit', 'is hit', 'blackcocktail', 'its not',
198+
],
199+
200+
/*
201+
|--------------------------------------------------------------------------
202+
| Global Allow List
203+
|--------------------------------------------------------------------------
204+
|
205+
| Words in this list will never be flagged as profanity.
206+
|
207+
*/
208+
'allow' => [],
209+
210+
/*
211+
|--------------------------------------------------------------------------
212+
| Global Block List
213+
|--------------------------------------------------------------------------
214+
|
215+
| Additional words to always flag as profanity.
216+
|
217+
*/
218+
'block' => [],
219+
220+
/*
221+
|--------------------------------------------------------------------------
222+
| Backward Compatibility: Profanities
223+
|--------------------------------------------------------------------------
224+
|
225+
| Basic profanity list for backward compatibility.
226+
| Full lists are in config/languages/*.php
227+
|
228+
*/
229+
'profanities' => [
230+
'fuck', 'shit', 'damn', 'bitch', 'ass', 'hell',
231+
],
232+
233+
];

0 commit comments

Comments
 (0)