this repo has no description
1/* 2 * CONTENT WARNING 3 * 4 * This file contains explicit slurs and hateful language. We're sorry you have to see them. 5 * 6 * These words exist here for one reason: to ensure our moderation system correctly blocks them. 7 * We can't verify the filter catches the n-word without testing against the actual word. 8 * Euphemisms wouldn't prove the protection works. 9 * 10 * If reading this file has caused you distress, please know: 11 * - you are valued and welcome in this community 12 * - these words do not reflect the views of this project or its contributors 13 * - we maintain this code precisely because we believe everyone deserves an experience on the web that is free from this kinda language 14*/ 15 16use regex::Regex; 17use std::sync::OnceLock; 18 19static SLUR_REGEXES: OnceLock<Vec<Regex>> = OnceLock::new(); 20static EXTRA_BANNED_WORDS: OnceLock<Vec<String>> = OnceLock::new(); 21 22fn get_slur_regexes() -> &'static Vec<Regex> { 23 SLUR_REGEXES.get_or_init(|| { 24 vec![ 25 Regex::new(r"\b[cĆćĈĉČčĊċÇçḈḉȻȼꞒꞓꟄꞔƇƈɕ][hĤĥȞȟḦḧḢḣḨḩḤḥḪḫH̱ẖĦħⱧⱨꞪɦꞕΗНн][iÍíi̇́Ììi̇̀ĬĭÎîǏǐÏïḮḯĨĩi̇̃ĮįĮ́į̇́Į̃į̇̃ĪīĪ̀ī̀ỈỉȈȉI̋i̋ȊȋỊịꞼꞽḬḭƗɨᶖİiIıIi1lĺľļḷḹl̃ḽḻłŀƚꝉⱡɫɬꞎꬷꬸꬹᶅɭȴLl][nŃńǸǹŇňÑñṄṅŅņṆṇṊṋṈṉN̈n̈ƝɲŊŋꞐꞑꞤꞥᵰᶇɳȵꬻꬼИиПпNn][kḰḱǨǩĶķḲḳḴḵƘƙⱩⱪᶄꝀꝁꝂꝃꝄꝅꞢꞣ][sŚśṤṥŜŝŠšṦṧṠṡŞşṢṣṨṩȘșS̩s̩ꞨꞩⱾȿꟅʂᶊᵴ]?\b").unwrap(), 26 Regex::new(r"\b[cĆćĈĉČčĊċÇçḈḉȻȼꞒꞓꟄꞔƇƈɕ][ÓóÒòŎŏÔôỐốỒồỖỗỔổǑǒÖöȪȫŐőÕõṌṍṎṏȬȭȮȯO͘o͘ȰȱØøǾǿǪǫǬǭŌōṒṓṐṑỎỏȌȍȎȏƠơỚớỜờỠỡỞởỢợỌọỘộO̩o̩Ò̩ò̩Ó̩ó̩ƟɵꝊꝋꝌꝍⱺOo0]{2}[nŃńǸǹŇňÑñṄṅŅņṆṇṊṋṈṉN̈n̈ƝɲŊŋꞐꞑꞤꞥᵰᶇɳȵꬻꬼИиПпNn][sŚśṤṥŜŝŠšṦṧṠṡŞşṢṣṨṩȘșS̩s̩ꞨꞩⱾȿꟅʂᶊᵴ]?\b").unwrap(), 27 Regex::new(r"\b[fḞḟƑƒꞘꞙᵮᶂ][aÁáÀàĂăẮắẰằẴẵẲẳÂâẤấẦầẪẫẨẩǍǎÅåǺǻÄäǞǟÃãȦȧǠǡĄąĄ́ą́Ą̃ą̃ĀāĀ̀ā̀ẢảȀȁA̋a̋ȂȃẠạẶặẬậḀḁȺⱥꞺꞻᶏẚAa@4][gǴǵĞğĜĝǦǧĠġG̃g̃ĢģḠḡǤǥꞠꞡƓɠᶃꬶGg]{1,2}([ÓóÒòŎŏÔôỐốỒồỖỗỔổǑǒÖöȪȫŐőÕõṌṍṎṏȬȭȮȯO͘o͘ȰȱØøǾǿǪǫǬǭŌōṒṓṐṑỎỏȌȍȎȏƠơỚớỜờỠỡỞởỢợỌọỘộO̩o̩Ò̩ò̩Ó̩ó̩ƟɵꝊꝋꝌꝍⱺOo0e3ЄєЕеÉéÈèĔĕÊêẾếỀềỄễỂểÊ̄ê̄Ê̌ê̌ĚěËëẼẽĖėĖ́ė́Ė̃ė̃ȨȩḜḝĘęĘ́ę́Ę̃ę̃ĒēḖḗḔḕẺẻȄȅE̋e̋ȆȇẸẹỆệḘḙḚḛɆɇE̩e̩È̩è̩É̩é̩ᶒⱸꬴꬳEeiÍíi̇́Ììi̇̀ĬĭÎîǏǐÏïḮḯĨĩi̇̃ĮįĮ́į̇́Į̃į̇̃ĪīĪ̀ī̀ỈỉȈȉI̋i̋ȊȋỊịꞼꞽḬḭƗɨᶖİiIıIi1lĺľļḷḹl̃ḽḻłŀƚꝉⱡɫɬꞎꬷꬸꬹᶅɭȴLl][tŤťṪṫŢţṬṭȚțṰṱṮṯŦŧȾⱦƬƭƮʈT̈ẗᵵƫȶ]{1,2}([rŔŕŘřṘṙŖŗȐȑȒȓṚṛṜṝṞṟR̃r̃ɌɍꞦꞧⱤɽᵲᶉꭉ][yÝýỲỳŶŷY̊ẙŸÿỸỹẎẏȲȳỶỷỴỵɎɏƳƴỾỿ]|[rŔŕŘřṘṙŖŗȐȑȒȓṚṛṜṝṞṟR̃r̃ɌɍꞦꞧⱤɽᵲᶉꭉ][iÍíi̇́Ììi̇̀ĬĭÎîǏǐÏïḮḯĨĩi̇̃ĮįĮ́į̇́Į̃į̇̃ĪīĪ̀ī̀ỈỉȈȉI̋i̋ȊȋỊịꞼꞽḬḭƗɨᶖİiIıIi1lĺľļḷḹl̃ḽḻłŀƚꝉⱡɫɬꞎꬷꬸꬹᶅɭȴLl][e3ЄєЕеÉéÈèĔĕÊêẾếỀềỄễỂểÊ̄ê̄Ê̌ê̌ĚěËëẼẽĖėĖ́ė́Ė̃ė̃ȨȩḜḝĘęĘ́ę́Ę̃ę̃ĒēḖḗḔḕẺẻȄȅE̋e̋ȆȇẸẹỆệḘḙḚḛɆɇE̩e̩È̩è̩É̩é̩ᶒⱸꬴꬳEe])?)?[sŚśṤṥŜŝŠšṦṧṠṡŞşṢṣṨṩȘșS̩s̩ꞨꞩⱾȿꟅʂᶊᵴ]?\b").unwrap(), 28 Regex::new(r"\b[kḰḱǨǩĶķḲḳḴḵƘƙⱩⱪᶄꝀꝁꝂꝃꝄꝅꞢꞣ][iÍíi̇́Ììi̇̀ĬĭÎîǏǐÏïḮḯĨĩi̇̃ĮįĮ́į̇́Į̃į̇̃ĪīĪ̀ī̀ỈỉȈȉI̋i̋ȊȋỊịꞼꞽḬḭƗɨᶖİiIıIi1lĺľļḷḹl̃ḽḻłŀƚꝉⱡɫɬꞎꬷꬸꬹᶅɭȴLlyÝýỲỳŶŷY̊ẙŸÿỸỹẎẏȲȳỶỷỴỵɎɏƳƴỾỿ][kḰḱǨǩĶķḲḳḴḵƘƙⱩⱪᶄꝀꝁꝂꝃꝄꝅꞢꞣ][e3ЄєЕеÉéÈèĔĕÊêẾếỀềỄễỂểÊ̄ê̄Ê̌ê̌ĚěËëẼẽĖėĖ́ė́Ė̃ė̃ȨȩḜḝĘęĘ́ę́Ę̃ę̃ĒēḖḗḔḕẺẻȄȅE̋e̋ȆȇẸẹỆệḘḙḚḛɆɇE̩e̩È̩è̩É̩é̩ᶒⱸꬴꬳEe]([rŔŕŘřṘṙŖŗȐȑȒȓṚṛṜṝṞṟR̃r̃ɌɍꞦꞧⱤɽᵲᶉꭉ][yÝýỲỳŶŷY̊ẙŸÿỸỹẎẏȲȳỶỷỴỵɎɏƳƴỾỿ]|[rŔŕŘřṘṙŖŗȐȑȒȓṚṛṜṝṞṟR̃r̃ɌɍꞦꞧⱤɽᵲᶉꭉ][iÍíi̇́Ììi̇̀ĬĭÎîǏǐÏïḮḯĨĩi̇̃ĮįĮ́į̇́Į̃į̇̃ĪīĪ̀ī̀ỈỉȈȉI̋i̋ȊȋỊịꞼꞽḬḭƗɨᶖİiIıIi1lĺľļḷḹl̃ḽḻłŀƚꝉⱡɫɬꞎꬷꬸꬹᶅɭȴLl][e3ЄєЕеÉéÈèĔĕÊêẾếỀềỄễỂểÊ̄ê̄Ê̌ê̌ĚěËëẼẽĖėĖ́ė́Ė̃ė̃ȨȩḜḝĘęĘ́ę́Ę̃ę̃ĒēḖḗḔḕẺẻȄȅE̋e̋ȆȇẸẹỆệḘḙḚḛɆɇE̩e̩È̩è̩É̩é̩ᶒⱸꬴꬳEe])?[sŚśṤṥŜŝŠšṦṧṠṡŞşṢṣṨṩȘșS̩s̩ꞨꞩⱾȿꟅʂᶊᵴ]*\b").unwrap(), 29 Regex::new(r"\b[nŃńǸǹŇňÑñṄṅŅņṆṇṊṋṈṉN̈n̈ƝɲŊŋꞐꞑꞤꞥᵰᶇɳȵꬻꬼИиПпNn][iÍíi̇́Ììi̇̀ĬĭÎîǏǐÏïḮḯĨĩi̇̃ĮįĮ́į̇́Į̃į̇̃ĪīĪ̀ī̀ỈỉȈȉI̋i̋ȊȋỊịꞼꞽḬḭƗɨᶖİiIıIi1lĺľļḷḹl̃ḽḻłŀƚꝉⱡɫɬꞎꬷꬸꬹᶅɭȴLloÓóÒòŎŏÔôỐốỒồỖỗỔổǑǒÖöȪȫŐőÕõṌṍṎṏȬȭȮȯO͘o͘ȰȱØøǾǿǪǫǬǭŌōṒṓṐṑỎỏȌȍȎȏƠơỚớỜờỠỡỞởỢợỌọỘộO̩o̩Ò̩ò̩Ó̩ó̩ƟɵꝊꝋꝌꝍⱺOoІіa4ÁáÀàĂăẮắẰằẴẵẲẳÂâẤấẦầẪẫẨẩǍǎÅåǺǻÄäǞǟÃãȦȧǠǡĄąĄ́ą́Ą̃ą̃ĀāĀ̀ā̀ẢảȀȁA̋a̋ȂȃẠạẶặẬậḀḁȺⱥꞺꞻᶏẚAa][gǴǵĞğĜĝǦǧĠġG̃g̃ĢģḠḡǤǥꞠꞡƓɠᶃꬶGgqꝖꝗꝘꝙɋʠ]{2}(l[e3ЄєЕеÉéÈèĔĕÊêẾếỀềỄễỂểÊ̄ê̄Ê̌ê̌ĚěËëẼẽĖėĖ́ė́Ė̃ė̃ȨȩḜḝĘęĘ́ę́Ę̃ę̃ĒēḖḗḔḕẺẻȄȅE̋e̋ȆȇẸẹỆệḘḙḚḛɆɇE̩e̩È̩è̩É̩é̩ᶒⱸꬴꬳEe]t|[e3ЄєЕеÉéÈèĔĕÊêẾếỀềỄễỂểÊ̄ê̄Ê̌ê̌ĚěËëẼẽĖėĖ́ė́Ė̃ė̃ȨȩḜḝĘęĘ́ę́Ę̃ę̃ĒēḖḗḔḕẺẻȄȅE̋e̋ȆȇẸẹỆệḘḙḚḛɆɇE̩e̩È̩è̩É̩é̩ᶒⱸꬴꬳEeaÁáÀàĂăẮắẰằẴẵẲẳÂâẤấẦầẪẫẨẩǍǎÅåǺǻÄäǞǟÃãȦȧǠǡĄąĄ́ą́Ą̃ą̃ĀāĀ̀ā̀ẢảȀȁA̋a̋ȂȃẠạẶặẬậḀḁȺⱥꞺꞻᶏẚAa][rŔŕŘřṘṙŖŗȐȑȒȓṚṛṜṝṞṟR̃r̃ɌɍꞦꞧⱤɽᵲᶉꭉ]?|n[ÓóÒòŎŏÔôỐốỒồỖỗỔổǑǒÖöȪȫŐőÕõṌṍṎṏȬȭȮȯO͘o͘ȰȱØøǾǿǪǫǬǭŌōṒṓṐṑỎỏȌȍȎȏƠơỚớỜờỠỡỞởỢợỌọỘộO̩o̩Ò̩ò̩Ó̩ó̩ƟɵꝊꝋꝌꝍⱺOo0][gǴǵĞğĜĝǦǧĠġG̃g̃ĢģḠḡǤǥꞠꞡƓɠᶃꬶGgqꝖꝗꝘꝙɋʠ]|[a4ÁáÀàĂăẮắẰằẴẵẲẳÂâẤấẦầẪẫẨẩǍǎÅåǺǻÄäǞǟÃãȦȧǠǡĄąĄ́ą́Ą̃ą̃ĀāĀ̀ā̀ẢảȀȁA̋a̋ȂȃẠạẶặẬậḀḁȺⱥꞺꞻᶏẚAa]?)?[sŚśṤṥŜŝŠšṦṧṠṡŞşṢṣṨṩȘșS̩s̩ꞨꞩⱾȿꟅʂᶊᵴ]?\b").unwrap(), 30 Regex::new(r"[nŃńǸǹŇňÑñṄṅŅņṆṇṊṋṈṉN̈n̈ƝɲŊŋꞐꞑꞤꞥᵰᶇɳȵꬻꬼИиПпNn][iÍíi̇́Ììi̇̀ĬĭÎîǏǐÏïḮḯĨĩi̇̃ĮįĮ́į̇́Į̃į̇̃ĪīĪ̀ī̀ỈỉȈȉI̋i̋ȊȋỊịꞼꞽḬḭƗɨᶖİiIıIi1lĺľļḷḹl̃ḽḻłŀƚꝉⱡɫɬꞎꬷꬸꬹᶅɭȴLloÓóÒòŎŏÔôỐốỒồỖỗỔổǑǒÖöȪȫŐőÕõṌṍṎṏȬȭȮȯO͘o͘ȰȱØøǾǿǪǫǬǭŌōṒṓṐṑỎỏȌȍȎȏƠơỚớỜờỠỡỞởỢợỌọỘộO̩o̩Ò̩ò̩Ó̩ó̩ƟɵꝊꝋꝌꝍⱺOoІіa4ÁáÀàĂăẮắẰằẴẵẲẳÂâẤấẦầẪẫẨẩǍǎÅåǺǻÄäǞǟÃãȦȧǠǡĄąĄ́ą́Ą̃ą̃ĀāĀ̀ā̀ẢảȀȁA̋a̋ȂȃẠạẶặẬậḀḁȺⱥꞺꞻᶏẚAa][gǴǵĞğĜĝǦǧĠġG̃g̃ĢģḠḡǤǥꞠꞡƓɠᶃꬶGgqꝖꝗꝘꝙɋʠ]{2}(l[e3ЄєЕеÉéÈèĔĕÊêẾếỀềỄễỂểÊ̄ê̄Ê̌ê̌ĚěËëẼẽĖėĖ́ė́Ė̃ė̃ȨȩḜḝĘęĘ́ę́Ę̃ę̃ĒēḖḗḔḕẺẻȄȅE̋e̋ȆȇẸẹỆệḘḙḚḛɆɇE̩e̩È̩è̩É̩é̩ᶒⱸꬴꬳEe]t|[e3ЄєЕеÉéÈèĔĕÊêẾếỀềỄễỂểÊ̄ê̄Ê̌ê̌ĚěËëẼẽĖėĖ́ė́Ė̃ė̃ȨȩḜḝĘęĘ́ę́Ę̃ę̃ĒēḖḗḔḕẺẻȄȅE̋e̋ȆȇẸẹỆệḘḙḚḛɆɇE̩e̩È̩è̩É̩é̩ᶒⱸꬴꬳEe][rŔŕŘřṘṙŖŗȐȑȒȓṚṛṜṝṞṟR̃r̃ɌɍꞦꞧⱤɽᵲᶉꭉ])[sŚśṤṥŜŝŠšṦṧṠṡŞşṢṣṨṩȘșS̩s̩ꞨꞩⱾȿꟅʂᶊᵴ]?").unwrap(), 31 Regex::new(r"\b[tŤťṪṫŢţṬṭȚțṰṱṮṯŦŧȾⱦƬƭƮʈT̈ẗᵵƫȶ][rŔŕŘřṘṙŖŗȐȑȒȓṚṛṜṝṞṟR̃r̃ɌɍꞦꞧⱤɽᵲᶉꭉ][aÁáÀàĂăẮắẰằẴẵẲẳÂâẤấẦầẪẫẨẩǍǎÅåǺǻÄäǞǟÃãȦȧǠǡĄąĄ́ą́Ą̃ą̃ĀāĀ̀ā̀ẢảȀȁA̋a̋ȂȃẠạẶặẬậḀḁȺⱥꞺꞻᶏẚAa4]+[nŃńǸǹŇňÑñṄṅŅņṆṇṊṋṈṉN̈n̈ƝɲŊŋꞐꞑꞤꞥᵰᶇɳȵꬻꬼИиПпNn]{1,2}([iÍíi̇́Ììi̇̀ĬĭÎîǏǐÏïḮḯĨĩi̇̃ĮįĮ́į̇́Į̃į̇̃ĪīĪ̀ī̀ỈỉȈȉI̋i̋ȊȋỊịꞼꞽḬḭƗɨᶖİiIıIi1lĺľļḷḹl̃ḽḻłŀƚꝉⱡɫɬꞎꬷꬸꬹᶅɭȴLl][e3ЄєЕеÉéÈèĔĕÊêẾếỀềỄễỂểÊ̄ê̄Ê̌ê̌ĚěËëẼẽĖėĖ́ė́Ė̃ė̃ȨȩḜḝĘęĘ́ę́Ę̃ę̃ĒēḖḗḔḕẺẻȄȅE̋e̋ȆȇẸẹỆệḘḙḚḛɆɇE̩e̩È̩è̩É̩é̩ᶒⱸꬴꬳEe]|[yÝýỲỳŶŷY̊ẙŸÿỸỹẎẏȲȳỶỷỴỵɎɏƳƴỾỿ]|[e3ЄєЕеÉéÈèĔĕÊêẾếỀềỄễỂểÊ̄ê̄Ê̌ê̌ĚěËëẼẽĖėĖ́ė́Ė̃ė̃ȨȩḜḝĘęĘ́ę́Ę̃ę̃ĒēḖḗḔḕẺẻȄȅE̋e̋ȆȇẸẹỆệḘḙḚḛɆɇE̩e̩È̩è̩É̩é̩ᶒⱸꬴꬳEe][rŔŕŘřṘṙŖŗȐȑȒȓṚṛṜṝṞṟR̃r̃ɌɍꞦꞧⱤɽᵲᶉꭉ])[sŚśṤṥŜŝŠšṦṧṠṡŞşṢṣṨṩȘșS̩s̩ꞨꞩⱾȿꟅʂᶊᵴ]?\b").unwrap(), 32 ] 33 }) 34} 35 36fn get_extra_banned_words() -> &'static Vec<String> { 37 EXTRA_BANNED_WORDS.get_or_init(|| { 38 std::env::var("PDS_BANNED_WORDS") 39 .unwrap_or_default() 40 .split(',') 41 .map(|s| s.trim().to_lowercase()) 42 .filter(|s| !s.is_empty()) 43 .collect() 44 }) 45} 46 47fn strip_trailing_digits(s: &str) -> &str { 48 s.trim_end_matches(|c: char| c.is_ascii_digit()) 49} 50 51fn normalize_leetspeak(s: &str) -> String { 52 s.chars() 53 .map(|c| match c { 54 '4' | '@' => 'a', 55 '3' => 'e', 56 '1' | '!' | '|' => 'i', 57 '0' => 'o', 58 '5' | '$' => 's', 59 '7' => 't', 60 '8' => 'b', 61 '9' => 'g', 62 _ => c, 63 }) 64 .collect() 65} 66 67pub fn has_explicit_slur(text: &str) -> bool { 68 has_explicit_slur_with_extra_words(text, get_extra_banned_words()) 69} 70 71fn has_explicit_slur_with_extra_words(text: &str, extra_words: &[String]) -> bool { 72 let text_lower = text.to_lowercase(); 73 let normalized = text_lower.replace('.', "").replace('-', "").replace('_', ""); 74 let stripped = strip_trailing_digits(&text_lower); 75 let normalized_stripped = strip_trailing_digits(&normalized); 76 77 let regexes = get_slur_regexes(); 78 if regexes.iter().any(|r| { 79 r.is_match(&text_lower) 80 || r.is_match(&normalized) 81 || r.is_match(stripped) 82 || r.is_match(normalized_stripped) 83 }) { 84 return true; 85 } 86 87 if !extra_words.is_empty() { 88 let leet_normalized = normalize_leetspeak(&normalized); 89 let leet_stripped = normalize_leetspeak(strip_trailing_digits(&leet_normalized)); 90 if extra_words.iter().any(|w| { 91 text_lower.contains(w) 92 || normalized.contains(w) 93 || stripped.contains(w) 94 || normalized_stripped.contains(w) 95 || leet_normalized.contains(w) 96 || leet_stripped.contains(w) 97 }) { 98 return true; 99 } 100 } 101 false 102} 103 104#[cfg(test)] 105mod tests { 106 use super::*; 107 108 #[test] 109 fn test_chink_pattern() { 110 assert!(has_explicit_slur("chink")); 111 assert!(has_explicit_slur("chinks")); 112 assert!(has_explicit_slur("CHINK")); 113 assert!(has_explicit_slur("Chinks")); 114 } 115 116 #[test] 117 fn test_coon_pattern() { 118 assert!(has_explicit_slur("coon")); 119 assert!(has_explicit_slur("coons")); 120 assert!(has_explicit_slur("COON")); 121 } 122 123 #[test] 124 fn test_fag_pattern() { 125 assert!(has_explicit_slur("fag")); 126 assert!(has_explicit_slur("fags")); 127 assert!(has_explicit_slur("faggot")); 128 assert!(has_explicit_slur("faggots")); 129 assert!(has_explicit_slur("faggotry")); 130 } 131 132 #[test] 133 fn test_kike_pattern() { 134 assert!(has_explicit_slur("kike")); 135 assert!(has_explicit_slur("kikes")); 136 assert!(has_explicit_slur("KIKE")); 137 assert!(has_explicit_slur("kikery")); 138 } 139 140 #[test] 141 fn test_nigger_pattern() { 142 assert!(has_explicit_slur("nigger")); 143 assert!(has_explicit_slur("niggers")); 144 assert!(has_explicit_slur("NIGGER")); 145 assert!(has_explicit_slur("nigga")); 146 assert!(has_explicit_slur("niggas")); 147 } 148 149 #[test] 150 fn test_tranny_pattern() { 151 assert!(has_explicit_slur("tranny")); 152 assert!(has_explicit_slur("trannies")); 153 assert!(has_explicit_slur("TRANNY")); 154 } 155 156 #[test] 157 fn test_normalization_bypass() { 158 assert!(has_explicit_slur("n.i.g.g.e.r")); 159 assert!(has_explicit_slur("n-i-g-g-e-r")); 160 assert!(has_explicit_slur("n_i_g_g_e_r")); 161 assert!(has_explicit_slur("f.a.g")); 162 assert!(has_explicit_slur("f-a-g")); 163 assert!(has_explicit_slur("c.h.i.n.k")); 164 assert!(has_explicit_slur("k_i_k_e")); 165 } 166 167 #[test] 168 fn test_trailing_digits_bypass() { 169 assert!(has_explicit_slur("faggot123")); 170 assert!(has_explicit_slur("nigger69")); 171 assert!(has_explicit_slur("chink420")); 172 assert!(has_explicit_slur("fag1")); 173 assert!(has_explicit_slur("kike2024")); 174 assert!(has_explicit_slur("n_i_g_g_e_r123")); 175 } 176 177 #[test] 178 fn test_embedded_in_sentence() { 179 assert!(has_explicit_slur("you are a faggot")); 180 assert!(has_explicit_slur("stupid nigger")); 181 assert!(has_explicit_slur("go away chink")); 182 } 183 184 #[test] 185 fn test_safe_words_not_matched() { 186 assert!(!has_explicit_slur("hello")); 187 assert!(!has_explicit_slur("world")); 188 assert!(!has_explicit_slur("bluesky")); 189 assert!(!has_explicit_slur("tranquil")); 190 assert!(!has_explicit_slur("programmer")); 191 assert!(!has_explicit_slur("trigger")); 192 assert!(!has_explicit_slur("bigger")); 193 assert!(!has_explicit_slur("digger")); 194 assert!(!has_explicit_slur("figure")); 195 assert!(!has_explicit_slur("configure")); 196 } 197 198 #[test] 199 fn test_similar_but_safe_words() { 200 assert!(!has_explicit_slur("niggardly")); 201 assert!(!has_explicit_slur("raccoon")); 202 } 203 204 #[test] 205 fn test_empty_and_whitespace() { 206 assert!(!has_explicit_slur("")); 207 assert!(!has_explicit_slur(" ")); 208 assert!(!has_explicit_slur("\t\n")); 209 } 210 211 #[test] 212 fn test_case_insensitive() { 213 assert!(has_explicit_slur("NIGGER")); 214 assert!(has_explicit_slur("Nigger")); 215 assert!(has_explicit_slur("NiGgEr")); 216 assert!(has_explicit_slur("FAGGOT")); 217 assert!(has_explicit_slur("Faggot")); 218 } 219 220 #[test] 221 fn test_leetspeak_bypass() { 222 assert!(has_explicit_slur("f4ggot")); 223 assert!(has_explicit_slur("f4gg0t")); 224 assert!(has_explicit_slur("n1gger")); 225 assert!(has_explicit_slur("n1gg3r")); 226 assert!(has_explicit_slur("k1ke")); 227 assert!(has_explicit_slur("ch1nk")); 228 assert!(has_explicit_slur("tr4nny")); 229 } 230 231 #[test] 232 fn test_normalize_leetspeak() { 233 assert_eq!(normalize_leetspeak("h3llo"), "hello"); 234 assert_eq!(normalize_leetspeak("w0rld"), "world"); 235 assert_eq!(normalize_leetspeak("t3$t"), "test"); 236 assert_eq!(normalize_leetspeak("b4dw0rd"), "badword"); 237 assert_eq!(normalize_leetspeak("l33t5p34k"), "leetspeak"); 238 assert_eq!(normalize_leetspeak("@ss"), "ass"); 239 assert_eq!(normalize_leetspeak("sh!t"), "shit"); 240 assert_eq!(normalize_leetspeak("normal"), "normal"); 241 } 242 243 #[test] 244 fn test_extra_banned_words() { 245 let extra = vec!["badword".to_string(), "offensive".to_string()]; 246 247 assert!(has_explicit_slur_with_extra_words("badword", &extra)); 248 assert!(has_explicit_slur_with_extra_words("BADWORD", &extra)); 249 assert!(has_explicit_slur_with_extra_words("b.a.d.w.o.r.d", &extra)); 250 assert!(has_explicit_slur_with_extra_words("b-a-d-w-o-r-d", &extra)); 251 assert!(has_explicit_slur_with_extra_words("b_a_d_w_o_r_d", &extra)); 252 assert!(has_explicit_slur_with_extra_words("badword123", &extra)); 253 assert!(has_explicit_slur_with_extra_words("b4dw0rd", &extra)); 254 assert!(has_explicit_slur_with_extra_words("b4dw0rd789", &extra)); 255 assert!(has_explicit_slur_with_extra_words("b.4.d.w.0.r.d", &extra)); 256 assert!(has_explicit_slur_with_extra_words("this contains badword here", &extra)); 257 assert!(has_explicit_slur_with_extra_words("0ff3n$1v3", &extra)); 258 259 assert!(!has_explicit_slur_with_extra_words("goodword", &extra)); 260 assert!(!has_explicit_slur_with_extra_words("hello world", &extra)); 261 } 262}