mirror of
https://github.com/sudoxnym/habitica-self-host.git
synced 2026-07-13 17:52:22 +00:00
lists banned words in the chat error message - fixes https://github.com/HabitRPG/habitica/issues/8812 (#8858)
* issue 8812 - added the list of bad words matched to the postChat error message. * issue 8812 - added the list of bad words matched to the postChat error message. * issue 8812 - some refactoring, fixed relevant tests, and lint rules refactor * small fix for unnecessary empty array * added test and did some small refactoring * lint error fix * issue 8812 - added the list of bad words matched to the postChat error message. * issue 8812 - some refactoring, fixed relevant tests, and lint rules refactor * small fix for unnecessary empty array * added test and did some small refactoring * lint error fix * add test to check the error message contains the banned words used * improve banned words test * issue 8812 - added the list of bad words matched to the postChat error message. * issue 8812 - some refactoring, fixed relevant tests, and lint rules refactor * small fix for unnecessary empty array * added test and did some small refactoring * lint error fix * issue 8812 - added the list of bad words matched to the postChat error message. * issue 8812 - some refactoring, fixed relevant tests, and lint rules refactor * add test to check the error message contains the banned words used * improve banned words test * merge with develop - aligned banned slurs check with banned words check
This commit is contained in:
parent
014a7197f0
commit
026014b8d6
3 changed files with 60 additions and 45 deletions
|
|
@ -10,6 +10,8 @@ import {
|
|||
TAVERN_ID,
|
||||
} from '../../../../../website/server/models/group';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
import { getMatchesByWordArray, removePunctuationFromString } from '../../../../../website/server/libs/stringUtils';
|
||||
import bannedWords from '../../../../../website/server/libs/bannedWords';
|
||||
import * as email from '../../../../../website/server/libs/email';
|
||||
import { IncomingWebhook } from '@slack/client';
|
||||
import nconf from 'nconf';
|
||||
|
|
@ -21,6 +23,9 @@ describe('POST /chat', () => {
|
|||
let testMessage = 'Test Message';
|
||||
let testBannedWordMessage = 'TEST_PLACEHOLDER_SWEAR_WORD_HERE';
|
||||
let testSlurMessage = 'message with TEST_PLACEHOLDER_SLUR_WORD_HERE';
|
||||
let bannedWordErrorMessage = t('bannedWordUsed').split('.');
|
||||
bannedWordErrorMessage[0] += ` (${removePunctuationFromString(testBannedWordMessage.toLowerCase())})`;
|
||||
bannedWordErrorMessage = bannedWordErrorMessage.join('.');
|
||||
|
||||
before(async () => {
|
||||
let { group, groupLeader, members } = await createAndPopulateGroup({
|
||||
|
|
@ -31,7 +36,6 @@ describe('POST /chat', () => {
|
|||
},
|
||||
members: 2,
|
||||
});
|
||||
|
||||
user = groupLeader;
|
||||
groupWithChat = group;
|
||||
member = members[0];
|
||||
|
|
@ -85,11 +89,11 @@ describe('POST /chat', () => {
|
|||
context('banned word', () => {
|
||||
it('returns an error when chat message contains a banned word in tavern', async () => {
|
||||
await expect(user.post('/groups/habitrpg/chat', { message: testBannedWordMessage}))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('bannedWordUsed'),
|
||||
});
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: bannedWordErrorMessage,
|
||||
});
|
||||
});
|
||||
|
||||
it('errors when word is part of a phrase', async () => {
|
||||
|
|
@ -98,7 +102,7 @@ describe('POST /chat', () => {
|
|||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('bannedWordUsed'),
|
||||
message: bannedWordErrorMessage,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -108,10 +112,26 @@ describe('POST /chat', () => {
|
|||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('bannedWordUsed'),
|
||||
message: bannedWordErrorMessage,
|
||||
});
|
||||
});
|
||||
|
||||
it('checks error message has the banned words used', async () => {
|
||||
let randIndex = Math.floor(Math.random() * (bannedWords.length + 1));
|
||||
let testBannedWords = bannedWords.slice(randIndex, randIndex + 2).map((w) => w.replace(/\\/g, ''));
|
||||
let chatMessage = `Mixing ${testBannedWords[0]} and ${testBannedWords[1]} is bad for you.`;
|
||||
await expect(user.post('/groups/habitrpg/chat', { message: chatMessage}))
|
||||
.to.eventually.be.rejected
|
||||
.and.have.property('message')
|
||||
.that.includes(testBannedWords.join(', '));
|
||||
});
|
||||
|
||||
it('check all banned words are matched', async () => {
|
||||
let message = bannedWords.join(',').replace(/\\/g, '');
|
||||
let matches = getMatchesByWordArray(message, bannedWords);
|
||||
expect(matches.length).to.equal(bannedWords.length);
|
||||
});
|
||||
|
||||
it('does not error when bad word is suffix of a word', async () => {
|
||||
let wordAsSuffix = `prefix${testBannedWordMessage}`;
|
||||
let message = await user.post('/groups/habitrpg/chat', { message: wordAsSuffix});
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import pusher from '../../libs/pusher';
|
|||
import nconf from 'nconf';
|
||||
import Bluebird from 'bluebird';
|
||||
import bannedWords from '../../libs/bannedWords';
|
||||
import { getMatchesByWordArray } from '../../libs/stringUtils';
|
||||
import { TAVERN_ID } from '../../models/group';
|
||||
import bannedSlurs from '../../libs/bannedSlurs';
|
||||
|
||||
|
|
@ -54,42 +55,9 @@ async function getAuthorEmailFromMessage (message) {
|
|||
}
|
||||
}
|
||||
|
||||
// @TODO: Probably move this to a library
|
||||
function matchExact (r, str) {
|
||||
let match = str.match(r);
|
||||
return match !== null && match[0] !== null;
|
||||
}
|
||||
|
||||
let bannedWordRegexs = [];
|
||||
for (let i = 0; i < bannedWords.length; i += 1) {
|
||||
let word = bannedWords[i];
|
||||
let regEx = new RegExp(`\\b([^a-z]+)?${word.toLowerCase()}([^a-z]+)?\\b`);
|
||||
bannedWordRegexs.push(regEx);
|
||||
}
|
||||
|
||||
function textContainsBannedWords (message) {
|
||||
for (let i = 0; i < bannedWordRegexs.length; i += 1) {
|
||||
let regEx = bannedWordRegexs[i];
|
||||
if (matchExact(regEx, message.toLowerCase())) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
let bannedSlurRegexs = [];
|
||||
for (let i = 0; i < bannedSlurs.length; i += 1) {
|
||||
let word = bannedSlurs[i];
|
||||
let regEx = new RegExp(`\\b([^a-z]+)?${word.toLowerCase()}([^a-z]+)?\\b`);
|
||||
bannedSlurRegexs.push(regEx);
|
||||
}
|
||||
|
||||
function textContainsBannedSlur (message) {
|
||||
for (let i = 0; i < bannedSlurRegexs.length; i += 1) {
|
||||
let regEx = bannedSlurRegexs[i];
|
||||
if (matchExact(regEx, message.toLowerCase())) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
let bannedSlursMatched = getMatchesByWordArray(message, bannedSlurs);
|
||||
return bannedSlursMatched.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -124,6 +92,10 @@ api.getChat = {
|
|||
},
|
||||
};
|
||||
|
||||
function getBannedWordsFromText (message) {
|
||||
return getMatchesByWordArray(message, bannedWords);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} /api/v3/groups/:groupId/chat Post chat message to a group
|
||||
* @apiName PostChat
|
||||
|
|
@ -199,8 +171,13 @@ api.postChat = {
|
|||
throw new NotAuthorized(res.t('chatPrivilegesRevoked'));
|
||||
}
|
||||
|
||||
if (group._id === TAVERN_ID && textContainsBannedWords(req.body.message)) {
|
||||
throw new BadRequest(res.t('bannedWordUsed'));
|
||||
if (group._id === TAVERN_ID) {
|
||||
let matchedBadWords = getBannedWordsFromText(req.body.message);
|
||||
if (matchedBadWords.length > 0) {
|
||||
let message = res.t('bannedWordUsed').split('.');
|
||||
message[0] += ` (${matchedBadWords.join(', ')})`;
|
||||
throw new BadRequest(message.join('.'));
|
||||
}
|
||||
}
|
||||
|
||||
let lastClientMsg = req.query.previousMsg;
|
||||
|
|
|
|||
18
website/server/libs/stringUtils.js
Normal file
18
website/server/libs/stringUtils.js
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
export function removePunctuationFromString (str) {
|
||||
return str.replace(/[.,\/#!@$%\^&;:{}=\-_`~()]/g, ' ');
|
||||
}
|
||||
|
||||
export function getMatchesByWordArray (str, wordsToMatch) {
|
||||
let matchedWords = [];
|
||||
let wordRegexs = wordsToMatch.map((word) => new RegExp(`\\b([^a-z]+)?${word.toLowerCase()}([^a-z]+)?\\b`));
|
||||
for (let i = 0; i < wordRegexs.length; i += 1) {
|
||||
let regEx = wordRegexs[i];
|
||||
let match = str.toLowerCase().match(regEx);
|
||||
if (match !== null && match[0] !== null) {
|
||||
let trimmedMatch = removePunctuationFromString(match[0]).trim();
|
||||
matchedWords.push(trimmedMatch);
|
||||
}
|
||||
}
|
||||
return matchedWords;
|
||||
}
|
||||
Loading…
Reference in a new issue