Add byte length checks to poll question and options during create

Co-authored-by: ayumi-signal <143036029+ayumi-signal@users.noreply.github.com>
This commit is contained in:
automated-signal
2025-11-12 09:41:40 -06:00
committed by GitHub
parent 8338227617
commit da43ea66a6
4 changed files with 47 additions and 11 deletions

View File

@@ -1534,6 +1534,14 @@
"messageformat": "Poll requires at least 2 options",
"description": "Error message shown when poll has fewer than 2 non-empty options"
},
"icu:PollCreateModal__Error--QuestionTooLong": {
"messageformat": "Question is too long",
"description": "Error message shown when poll question string exceeds the byte limit"
},
"icu:PollCreateModal__Error--OptionTooLong": {
"messageformat": "Option is too long",
"description": "Error message shown when poll option string exceeds the byte limit"
},
"icu:deleteConversation": {
"messageformat": "Delete",
"description": "Menu item for deleting a conversation (including messages), title case."

View File

@@ -23,6 +23,7 @@ import {
POLL_OPTIONS_MAX_COUNT,
} from '../types/Polls.dom.js';
import { count as countGraphemes } from '../util/grapheme.std.js';
import { MAX_MESSAGE_BODY_BYTE_LENGTH } from '../util/longAttachment.std.js';
type PollOption = {
id: string;
@@ -212,20 +213,34 @@ export function PollCreateModal({
hasQuestionError: boolean;
hasOptionsError: boolean;
} => {
const errors: Array<string> = [];
const hasQuestionError = !question.trim();
const nonEmptyOptions = options.filter(opt => opt.value.trim());
const hasOptionsError = nonEmptyOptions.length < POLL_OPTIONS_MIN_COUNT;
const questionErrors: Array<string> = [];
const optionErrors: Array<string> = [];
if (hasQuestionError) {
errors.push(i18n('icu:PollCreateModal__Error--RequiresQuestion'));
const questionValue = question.trim();
if (!questionValue) {
questionErrors.push(i18n('icu:PollCreateModal__Error--RequiresQuestion'));
}
if (Buffer.byteLength(questionValue) > MAX_MESSAGE_BODY_BYTE_LENGTH) {
questionErrors.push(i18n('icu:PollCreateModal__Error--QuestionTooLong'));
}
if (hasOptionsError) {
errors.push(i18n('icu:PollCreateModal__Error--RequiresTwoOptions'));
const optionValues = options.map(opt => opt.value.trim());
const nonEmptyOptions = optionValues.filter(value => value);
if (nonEmptyOptions.length < POLL_OPTIONS_MIN_COUNT) {
optionErrors.push(i18n('icu:PollCreateModal__Error--RequiresTwoOptions'));
}
const optionOverByteLength = optionValues.find(
value => Buffer.byteLength(value) > MAX_MESSAGE_BODY_BYTE_LENGTH
);
if (optionOverByteLength) {
optionErrors.push(i18n('icu:PollCreateModal__Error--OptionTooLong'));
}
return { errors, hasQuestionError, hasOptionsError };
return {
errors: questionErrors.concat(optionErrors),
hasQuestionError: questionErrors.length > 0,
hasOptionsError: optionErrors.length > 0,
};
}, [question, options, i18n]);
const handleSend = useCallback(() => {

View File

@@ -12,6 +12,7 @@ import * as RemoteConfig from '../RemoteConfig.dom.js';
import { isAlpha, isBeta, isProduction } from '../util/version.std.js';
import type { SendStateByConversationId } from '../messages/MessageSendState.std.js';
import { aciSchema } from './ServiceId.std.js';
import { MAX_MESSAGE_BODY_BYTE_LENGTH } from '../util/longAttachment.std.js';
export const POLL_QUESTION_MAX_LENGTH = 100;
export const POLL_OPTIONS_MIN_COUNT = 2;
@@ -28,7 +29,13 @@ export const PollCreateSchema = z
.min(1)
.refine(value => hasAtMostGraphemes(value, POLL_QUESTION_MAX_LENGTH), {
message: `question must contain at most ${POLL_QUESTION_MAX_LENGTH} characters`,
}),
})
.refine(
value => Buffer.byteLength(value) <= MAX_MESSAGE_BODY_BYTE_LENGTH,
{
message: `question must contain at most ${MAX_MESSAGE_BODY_BYTE_LENGTH} bytes`,
}
),
options: z
.array(
z
@@ -40,6 +47,12 @@ export const PollCreateSchema = z
message: `option must contain at most ${POLL_QUESTION_MAX_LENGTH} characters`,
}
)
.refine(
value => Buffer.byteLength(value) <= MAX_MESSAGE_BODY_BYTE_LENGTH,
{
message: `option must contain at most ${MAX_MESSAGE_BODY_BYTE_LENGTH} bytes`,
}
)
)
.min(POLL_OPTIONS_MIN_COUNT)
.max(POLL_OPTIONS_MAX_COUNT)

View File

@@ -4,7 +4,7 @@
import { unicodeSlice } from './unicodeSlice.std.js';
const KIBIBYTE = 1024;
const MAX_MESSAGE_BODY_BYTE_LENGTH = 2 * KIBIBYTE;
export const MAX_MESSAGE_BODY_BYTE_LENGTH = 2 * KIBIBYTE;
export const MAX_BODY_ATTACHMENT_BYTE_LENGTH = 64 * KIBIBYTE;