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:
borisabramovich86 2017-08-02 22:43:22 +03:00 committed by Sabe Jones
parent 014a7197f0
commit 026014b8d6
3 changed files with 60 additions and 45 deletions

View file

@ -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});

View file

@ -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;

View 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;
}