diff --git a/test/api/v3/integration/chat/POST-chat.test.js b/test/api/v3/integration/chat/POST-chat.test.js index 3069c245f0..db73179ed3 100644 --- a/test/api/v3/integration/chat/POST-chat.test.js +++ b/test/api/v3/integration/chat/POST-chat.test.js @@ -11,7 +11,7 @@ import { TAVERN_ID, } from '../../../../../website/server/models/group'; import { v4 as generateUUID } from 'uuid'; -import { getMatchesByWordArray, removePunctuationFromString } from '../../../../../website/server/libs/stringUtils'; +import { getMatchesByWordArray } from '../../../../../website/server/libs/stringUtils'; import bannedWords from '../../../../../website/server/libs/bannedWords'; import guildsAllowingBannedWords from '../../../../../website/server/libs/guildsAllowingBannedWords'; import * as email from '../../../../../website/server/libs/email'; @@ -24,10 +24,10 @@ describe('POST /chat', () => { let user, groupWithChat, member, additionalMember; let testMessage = 'Test Message'; let testBannedWordMessage = 'TESTPLACEHOLDERSWEARWORDHERE'; + let testBannedWordMessage1 = 'TESTPLACEHOLDERSWEARWORDHERE1'; let testSlurMessage = 'message with TESTPLACEHOLDERSLURWORDHERE'; - let bannedWordErrorMessage = t('bannedWordUsed').split('.'); - bannedWordErrorMessage[0] += ` (${removePunctuationFromString(testBannedWordMessage.toLowerCase())})`; - bannedWordErrorMessage = bannedWordErrorMessage.join('.'); + let testSlurMessage1 = 'TESTPLACEHOLDERSLURWORDHERE1'; + let bannedWordErrorMessage = t('bannedWordUsed', {swearWordsUsed: testBannedWordMessage}); before(async () => { let { group, groupLeader, members } = await createAndPopulateGroup({ @@ -39,6 +39,7 @@ describe('POST /chat', () => { members: 2, }); user = groupLeader; + await user.update({'contributor.level': SPAM_MIN_EXEMPT_CONTRIB_LEVEL}); // prevent tests accidentally throwing messageGroupChatSpam groupWithChat = group; member = members[0]; additionalMember = members[1]; @@ -136,9 +137,19 @@ describe('POST /chat', () => { }); }); - 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, '')); + it('errors when word is typed in mixed case', async () => { + let substrLength = Math.floor(testBannedWordMessage.length / 2); + let chatMessage = testBannedWordMessage.substring(0, substrLength).toLowerCase() + testBannedWordMessage.substring(substrLength).toUpperCase(); + await expect(user.post('/groups/habitrpg/chat', { message: chatMessage })) + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('bannedWordUsed', {swearWordsUsed: chatMessage}), + }); + }); + + it('checks error message has all the banned words used, regardless of case', async () => { + let testBannedWords = [testBannedWordMessage.toUpperCase(), testBannedWordMessage1.toLowerCase()]; 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 @@ -320,6 +331,17 @@ describe('POST /chat', () => { members[0].flags.chatRevoked = false; await members[0].update({'flags.chatRevoked': false}); }); + + it('errors when slur is typed in mixed case', async () => { + let substrLength = Math.floor(testSlurMessage1.length / 2); + let chatMessage = testSlurMessage1.substring(0, substrLength).toLowerCase() + testSlurMessage1.substring(substrLength).toUpperCase(); + await expect(user.post('/groups/habitrpg/chat', { message: chatMessage })) + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('bannedSlurUsed'), + }); + }); }); it('does not error when sending a message to a private guild with a user with revoked chat', async () => { diff --git a/website/common/locales/en/groups.json b/website/common/locales/en/groups.json index 05ffb84db1..1b2ea93bd8 100644 --- a/website/common/locales/en/groups.json +++ b/website/common/locales/en/groups.json @@ -34,7 +34,7 @@ "communityGuidelines": "Community Guidelines", "communityGuidelinesRead1": "Please read our", "communityGuidelinesRead2": "before chatting.", - "bannedWordUsed": "Oops! Looks like this post contains a swearword, religious oath, or reference to an addictive substance or adult topic. Habitica has users from all backgrounds, so we keep our chat very clean. Feel free to edit your message so you can post it!", + "bannedWordUsed": "Oops! Looks like this post contains a swearword, religious oath, or reference to an addictive substance or adult topic (<%= swearWordsUsed %>). Habitica has users from all backgrounds, so we keep our chat very clean. Feel free to edit your message so you can post it!", "bannedSlurUsed": "Your post contained inappropriate language, and your chat privileges have been revoked.", "party": "Party", "createAParty": "Create A Party", diff --git a/website/server/controllers/api-v3/chat.js b/website/server/controllers/api-v3/chat.js index de48a80c47..df622ced54 100644 --- a/website/server/controllers/api-v3/chat.js +++ b/website/server/controllers/api-v3/chat.js @@ -160,10 +160,7 @@ api.postChat = { if (group.privacy !== 'private' && !guildsAllowingBannedWords[group._id]) { let matchedBadWords = getBannedWordsFromText(req.body.message); if (matchedBadWords.length > 0) { - // @TODO replace this split mechanism with something that works properly in translations - let message = res.t('bannedWordUsed').split('.'); - message[0] += ` (${matchedBadWords.join(', ')})`; - throw new BadRequest(message.join('.')); + throw new BadRequest(res.t('bannedWordUsed', {swearWordsUsed: matchedBadWords.join(', ')})); } } diff --git a/website/server/libs/stringUtils.js b/website/server/libs/stringUtils.js index abed3995d6..f10deb99f0 100644 --- a/website/server/libs/stringUtils.js +++ b/website/server/libs/stringUtils.js @@ -5,10 +5,10 @@ export function removePunctuationFromString (str) { export function getMatchesByWordArray (str, wordsToMatch) { let matchedWords = []; - let wordRegexs = wordsToMatch.map((word) => new RegExp(`\\b([^a-z]+)?${word.toLowerCase()}([^a-z]+)?\\b`)); + let wordRegexs = wordsToMatch.map((word) => new RegExp(`\\b([^a-z]+)?${word}([^a-z]+)?\\b`, 'i')); for (let i = 0; i < wordRegexs.length; i += 1) { let regEx = wordRegexs[i]; - let match = str.toLowerCase().match(regEx); + let match = str.match(regEx); if (match !== null && match[0] !== null) { let trimmedMatch = removePunctuationFromString(match[0]).trim(); matchedWords.push(trimmedMatch);