Fix username links resulting in truncated chat messages (#11945)

* introduce MAX_MESSAGE_LENGTH constant

* add test

* fix path

* fix and tests

* fix typo in tests
This commit is contained in:
Matteo Pagliazzi
2020-03-04 11:49:14 +01:00
committed by GitHub
parent 75068ceb9e
commit 2ff9dfe965
9 changed files with 54 additions and 13 deletions

View File

@@ -13,7 +13,7 @@ import {
SPAM_MIN_EXEMPT_CONTRIB_LEVEL,
TAVERN_ID,
} from '../../../../../website/server/models/group';
import { CHAT_FLAG_FROM_SHADOW_MUTE } from '../../../../../website/common/script/constants';
import { CHAT_FLAG_FROM_SHADOW_MUTE, MAX_MESSAGE_LENGTH } from '../../../../../website/common/script/constants';
import { getMatchesByWordArray } from '../../../../../website/server/libs/stringUtils';
import bannedWords from '../../../../../website/server/libs/bannedWords';
import guildsAllowingBannedWords from '../../../../../website/server/libs/guildsAllowingBannedWords';
@@ -494,6 +494,7 @@ describe('POST /chat', () => {
123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789.
THIS PART WON'T BE IN THE MESSAGE (over 3000)
`;
expect(veryLongMessage.length > MAX_MESSAGE_LENGTH).to.equal(true);
const newMessage = await user.post(`/groups/${groupWithChat._id}/chat`, { message: veryLongMessage });
const groupMessages = await user.get(`/groups/${groupWithChat._id}/chat`);
@@ -501,9 +502,27 @@ describe('POST /chat', () => {
expect(newMessage.message.id).to.exist;
expect(groupMessages[0].id).to.exist;
expect(newMessage.message.text.length).to.eql(3000);
expect(newMessage.message.text.length).to.eql(MAX_MESSAGE_LENGTH);
expect(newMessage.message.text).to.not.contain('MESSAGE');
expect(groupMessages[0].text.length).to.eql(3000);
expect(groupMessages[0].text.length).to.eql(MAX_MESSAGE_LENGTH);
});
it('chat message with mentions - mention link should not count towards 3000 chars limit', async () => {
const memberUsername = 'memberUsername';
await member.update({ 'auth.local.username': memberUsername });
const messageWithMentions = `hi @${memberUsername} 123456789
123456789 123456789 123456789 123456789 123456789 123456789 89 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345678 END.`;
expect(messageWithMentions.length).to.equal(MAX_MESSAGE_LENGTH);
const newMessage = await user.post(`/groups/${groupWithChat._id}/chat`, { message: messageWithMentions });
const groupMessages = await user.get(`/groups/${groupWithChat._id}/chat`);
const mentionLink = `[@${memberUsername}](/profile/${member._id})`;
expect(newMessage.message.text).to.include(mentionLink);
expect(newMessage.message.text).to.include(' END.');
expect(newMessage.message.text.length)
.to.eql(messageWithMentions.length - (`@${memberUsername}`).length + mentionLink.length);
expect(groupMessages[0].text.length).to.eql(newMessage.message.text.length);
});
it('creates a chat with user styles', async () => {

View File

@@ -20,7 +20,7 @@
v-model="newMessage"
:placeholder="placeholder"
:class="{'user-entry': newMessage}"
maxlength="3000"
:maxlength="MAX_MESSAGE_LENGTH"
@keydown="updateCarretPosition"
@keyup.ctrl.enter="sendMessageShortcut()"
@keydown.tab="handleTab($event)"
@@ -30,7 +30,7 @@
@keydown.esc="handleEscape($event)"
@paste="disableMessageSendShortcut()"
></textarea>
<span>{{ currentLength }} / 3000</span>
<span>{{ currentLength }} / {{ MAX_MESSAGE_LENGTH }}</span>
<autocomplete
ref="autocomplete"
:text="newMessage"
@@ -91,6 +91,7 @@ import communityGuidelines from './communityGuidelines';
import chatMessage from '../chat/chatMessages';
import { mapState } from '@/libs/store';
import markdownDirective from '@/directives/markdown';
import { MAX_MESSAGE_LENGTH } from '@/../../common/script/constants';
export default {
directives: {
@@ -116,6 +117,7 @@ export default {
LEFT: 0,
},
textbox: this.$refs,
MAX_MESSAGE_LENGTH: MAX_MESSAGE_LENGTH.toString(),
};
},
computed: {

View File

@@ -141,7 +141,7 @@
v-model="newMessage"
class="flex-fill"
:placeholder="$t('needsTextPlaceholder')"
maxlength="3000"
:maxlength="MAX_MESSAGE_LENGTH"
:class="{'has-content': newMessage !== ''}"
@keyup.ctrl.enter="sendPrivateMessage()"
></textarea>
@@ -518,6 +518,7 @@ import groupBy from 'lodash/groupBy';
import orderBy from 'lodash/orderBy';
import habiticaMarkdown from 'habitica-markdown';
import axios from 'axios';
import { MAX_MESSAGE_LENGTH } from '@/../../common/script/constants';
import { mapState } from '@/libs/store';
import styleHelper from '@/mixins/styleHelper';
import toggleSwitch from '@/components/ui/toggleSwitch';
@@ -563,6 +564,7 @@ export default {
messagesLoading: false,
initiatedConversation: null,
updateConversationsCounter: 0,
MAX_MESSAGE_LENGTH: MAX_MESSAGE_LENGTH.toString(),
};
},
async mounted () {

View File

@@ -9,6 +9,7 @@ export const LARGE_GROUP_COUNT_MESSAGE_CUTOFF = 5000;
export const MAX_SUMMARY_SIZE_FOR_GUILDS = 250;
export const MAX_SUMMARY_SIZE_FOR_CHALLENGES = 250;
export const MIN_SHORTNAME_SIZE_FOR_CHALLENGES = 3;
export const MAX_MESSAGE_LENGTH = 3000;
export const CHAT_FLAG_LIMIT_FOR_HIDING = 2; // hide posts that have this many flags
export const CHAT_FLAG_FROM_MOD = 5; // a flag from a moderator counts as this many flags

View File

@@ -18,6 +18,7 @@ import {
MINIMUM_PASSWORD_LENGTH,
SUPPORTED_SOCIAL_NETWORKS,
TAVERN_ID,
MAX_MESSAGE_LENGTH,
} from './constants';
import content from './content/index';
import * as count from './count';
@@ -108,6 +109,7 @@ api.constants = {
CHAT_FLAG_FROM_MOD,
CHAT_FLAG_FROM_SHADOW_MUTE,
MINIMUM_PASSWORD_LENGTH,
MAX_MESSAGE_LENGTH,
};
// TODO Move these under api.constants
api.maxLevel = MAX_LEVEL;

View File

@@ -2,7 +2,10 @@ import nconf from 'nconf';
import { authWithHeaders } from '../../middlewares/auth';
import { model as Group } from '../../models/group';
import { model as User } from '../../models/user';
import { chatModel as Chat } from '../../models/message';
import {
chatModel as Chat,
sanitizeText as sanitizeMessageText,
} from '../../models/message';
import common from '../../../common';
import {
BadRequest,
@@ -187,7 +190,8 @@ api.postChat = {
throw new NotAuthorized(res.t('messageGroupChatSpam'));
}
const [message, mentions, mentionedMembers] = await highlightMentions(req.body.message);
const sanitizedMessageText = sanitizeMessageText(req.body.message);
const [message, mentions, mentionedMembers] = await highlightMentions(sanitizedMessageText);
let client = req.headers['x-client'] || '3rd Party';
if (client) {
client = client.replace('habitica-', '');

View File

@@ -21,6 +21,9 @@ import {
import { sendNotification as sendPushNotification } from '../../libs/pushNotifications';
import common from '../../../common';
import { sentMessage } from '../../libs/inbox';
import {
sanitizeText as sanitizeMessageText,
} from '../../models/message';
import { highlightMentions } from '../../libs/highlightMentions';
const { achievements } = common;
@@ -677,7 +680,8 @@ api.sendPrivateMessage = {
if (validationErrors) throw validationErrors;
const sender = res.locals.user;
const message = (await highlightMentions(req.body.message))[0];
const sanitizedMessageText = sanitizeMessageText(req.body.message);
const message = (await highlightMentions(sanitizedMessageText))[0];
const receiver = await User.findById(req.body.toUserId).exec();
if (!receiver) throw new NotFound(res.t('userNotFound'));

View File

@@ -3,6 +3,7 @@ import { v4 as uuid } from 'uuid';
import { defaults } from 'lodash';
import removeMd from 'remove-markdown';
import baseModel from '../libs/baseModel';
import shared from '../../common';
const defaultSchema = () => ({
id: String,
@@ -113,14 +114,20 @@ export function setUserStyles (newMessage, user) {
}
}
// Sanitize an input message, separate from messageDefaults because
// it must run before mentions are highlighted
export function sanitizeText (msg) {
// Trim messages longer than the MAX_MESSAGE_LENGTH
return msg.substring(0, shared.constants.MAX_MESSAGE_LENGTH);
}
export function messageDefaults (msg, user, client, flagCount = 0, info = {}) {
const id = uuid();
const trimmedMessage = msg.substring(0, 3000);
const message = {
id,
_id: id,
text: trimmedMessage,
unformattedText: removeMd(trimmedMessage),
text: msg,
unformattedText: removeMd(msg),
info,
timestamp: Number(new Date()),
likes: {},

View File

@@ -110,7 +110,7 @@ schema.methods.getObjectionsToInteraction = function getObjectionsToInteraction
/**
* Sends a message to a this. Archives a copy in sender's inbox.
* Sends a message to a user. Archives a copy in sender's inbox.
*
* @param userToReceiveMessage The receiver
* @param options