mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-09 16:13:31 +01:00
fix(builders): add proper snowflake validation (#11290)
* fix(builders): add proper snowflake validation close #11289 * fix(builders): use snowflake validation for attachment id * test(builders): add validation tests for snowflake attachment IDs * fix: better regex Co-authored-by: Almeida <github@almeidx.dev> * test(builders): fix snowflake validation in fileBody test * Update packages/builders/src/Assertions.ts Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com> * fix: update regex --------- Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com> Co-authored-by: Almeida <github@almeidx.dev> Co-authored-by: Vlad Frangu <me@vladfrangu.dev> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
@@ -6,7 +6,7 @@ import { AttachmentBuilder, MessageBuilder } from '../../src/index.js';
|
|||||||
test('AttachmentBuilder stores and exposes file data', () => {
|
test('AttachmentBuilder stores and exposes file data', () => {
|
||||||
const data = Buffer.from('hello world');
|
const data = Buffer.from('hello world');
|
||||||
const attachment = new AttachmentBuilder()
|
const attachment = new AttachmentBuilder()
|
||||||
.setId('0')
|
.setId(1)
|
||||||
.setFilename('greeting.txt')
|
.setFilename('greeting.txt')
|
||||||
.setFileData(data)
|
.setFileData(data)
|
||||||
.setFileContentType('text/plain');
|
.setFileContentType('text/plain');
|
||||||
@@ -14,7 +14,7 @@ test('AttachmentBuilder stores and exposes file data', () => {
|
|||||||
expect(attachment.getRawFile()).toStrictEqual({
|
expect(attachment.getRawFile()).toStrictEqual({
|
||||||
contentType: 'text/plain',
|
contentType: 'text/plain',
|
||||||
data,
|
data,
|
||||||
key: 'files[0]',
|
key: 'files[1]',
|
||||||
name: 'greeting.txt',
|
name: 'greeting.txt',
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ test('AttachmentBuilder stores and exposes file data', () => {
|
|||||||
test('MessageBuilder.toFileBody returns JSON body and files', () => {
|
test('MessageBuilder.toFileBody returns JSON body and files', () => {
|
||||||
const msg = new MessageBuilder().setContent('here is a file').addAttachments(
|
const msg = new MessageBuilder().setContent('here is a file').addAttachments(
|
||||||
new AttachmentBuilder()
|
new AttachmentBuilder()
|
||||||
.setId('0')
|
.setId(0)
|
||||||
.setFilename('file.bin')
|
.setFilename('file.bin')
|
||||||
.setFileData(Buffer.from([1, 2, 3]))
|
.setFileData(Buffer.from([1, 2, 3]))
|
||||||
.setFileContentType('application/octet-stream'),
|
.setFileContentType('application/octet-stream'),
|
||||||
@@ -47,7 +47,9 @@ test('MessageBuilder.toFileBody returns JSON body and files', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('MessageBuilder.toFileBody returns empty files when attachments reference existing uploads', () => {
|
test('MessageBuilder.toFileBody returns empty files when attachments reference existing uploads', () => {
|
||||||
const msg = new MessageBuilder().addAttachments(new AttachmentBuilder().setId('123').setFilename('existing.png'));
|
const msg = new MessageBuilder().addAttachments(
|
||||||
|
new AttachmentBuilder().setId('1234567890123456789').setFilename('existing.png'),
|
||||||
|
);
|
||||||
|
|
||||||
const { body, files } = msg.toFileBody();
|
const { body, files } = msg.toFileBody();
|
||||||
expect(body).toEqual(msg.toJSON());
|
expect(body).toEqual(msg.toJSON());
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ describe('Message', () => {
|
|||||||
row.addPrimaryButtonComponents((button) => button.setCustomId('abc').setLabel('def')),
|
row.addPrimaryButtonComponents((button) => button.setCustomId('abc').setLabel('def')),
|
||||||
)
|
)
|
||||||
.setStickerIds('123', '456')
|
.setStickerIds('123', '456')
|
||||||
.addAttachments((attachment) => attachment.setId('hi!').setFilename('abc'))
|
.addAttachments((attachment) => attachment.setId(0).setFilename('abc'))
|
||||||
.setFlags(MessageFlags.Ephemeral)
|
.setFlags(MessageFlags.Ephemeral)
|
||||||
.setEnforceNonce(false)
|
.setEnforceNonce(false)
|
||||||
.updatePoll((poll) => poll.addAnswers({ poll_media: { text: 'foo' } }).setQuestion({ text: 'foo' }));
|
.updatePoll((poll) => poll.addAnswers({ poll_media: { text: 'foo' } }).setQuestion({ text: 'foo' }));
|
||||||
@@ -83,7 +83,7 @@ describe('Message', () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
sticker_ids: ['123', '456'],
|
sticker_ids: ['123', '456'],
|
||||||
attachments: [{ id: 'hi!', filename: 'abc' }],
|
attachments: [{ id: 0, filename: 'abc' }],
|
||||||
flags: 64,
|
flags: 64,
|
||||||
enforce_nonce: false,
|
enforce_nonce: false,
|
||||||
poll: {
|
poll: {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { z } from 'zod';
|
|||||||
|
|
||||||
export const idPredicate = z.int().min(0).max(2_147_483_647).optional();
|
export const idPredicate = z.int().min(0).max(2_147_483_647).optional();
|
||||||
export const customIdPredicate = z.string().min(1).max(100);
|
export const customIdPredicate = z.string().min(1).max(100);
|
||||||
|
export const snowflakePredicate = z.string().regex(/^(?:0|[1-9]\d*)$/);
|
||||||
|
|
||||||
export const memberPermissionsPredicate = z.coerce.bigint();
|
export const memberPermissionsPredicate = z.coerce.bigint();
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { ButtonStyle, ChannelType, ComponentType, SelectMenuDefaultValueType } from 'discord-api-types/v10';
|
import { ButtonStyle, ChannelType, ComponentType, SelectMenuDefaultValueType } from 'discord-api-types/v10';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { idPredicate, customIdPredicate } from '../Assertions.js';
|
import { idPredicate, customIdPredicate, snowflakePredicate } from '../Assertions.js';
|
||||||
|
|
||||||
const labelPredicate = z.string().min(1).max(80);
|
const labelPredicate = z.string().min(1).max(80);
|
||||||
|
|
||||||
export const emojiPredicate = z
|
export const emojiPredicate = z
|
||||||
.strictObject({
|
.strictObject({
|
||||||
id: z.string().optional(),
|
id: snowflakePredicate.optional(),
|
||||||
name: z.string().min(2).max(32).optional(),
|
name: z.string().min(2).max(32).optional(),
|
||||||
animated: z.boolean().optional(),
|
animated: z.boolean().optional(),
|
||||||
})
|
})
|
||||||
@@ -39,7 +39,7 @@ const buttonLinkPredicate = buttonPredicateBase.extend({
|
|||||||
|
|
||||||
const buttonPremiumPredicate = buttonPredicateBase.extend({
|
const buttonPremiumPredicate = buttonPredicateBase.extend({
|
||||||
style: z.literal(ButtonStyle.Premium),
|
style: z.literal(ButtonStyle.Premium),
|
||||||
sku_id: z.string(),
|
sku_id: snowflakePredicate,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const buttonPredicate = z.discriminatedUnion('style', [
|
export const buttonPredicate = z.discriminatedUnion('style', [
|
||||||
@@ -64,7 +64,7 @@ export const selectMenuChannelPredicate = selectMenuBasePredicate.extend({
|
|||||||
type: z.literal(ComponentType.ChannelSelect),
|
type: z.literal(ComponentType.ChannelSelect),
|
||||||
channel_types: z.enum(ChannelType).array().optional(),
|
channel_types: z.enum(ChannelType).array().optional(),
|
||||||
default_values: z
|
default_values: z
|
||||||
.object({ id: z.string(), type: z.literal(SelectMenuDefaultValueType.Channel) })
|
.object({ id: snowflakePredicate, type: z.literal(SelectMenuDefaultValueType.Channel) })
|
||||||
.array()
|
.array()
|
||||||
.max(25)
|
.max(25)
|
||||||
.optional(),
|
.optional(),
|
||||||
@@ -74,7 +74,7 @@ export const selectMenuMentionablePredicate = selectMenuBasePredicate.extend({
|
|||||||
type: z.literal(ComponentType.MentionableSelect),
|
type: z.literal(ComponentType.MentionableSelect),
|
||||||
default_values: z
|
default_values: z
|
||||||
.object({
|
.object({
|
||||||
id: z.string(),
|
id: snowflakePredicate,
|
||||||
type: z.literal([SelectMenuDefaultValueType.Role, SelectMenuDefaultValueType.User]),
|
type: z.literal([SelectMenuDefaultValueType.Role, SelectMenuDefaultValueType.User]),
|
||||||
})
|
})
|
||||||
.array()
|
.array()
|
||||||
@@ -85,7 +85,7 @@ export const selectMenuMentionablePredicate = selectMenuBasePredicate.extend({
|
|||||||
export const selectMenuRolePredicate = selectMenuBasePredicate.extend({
|
export const selectMenuRolePredicate = selectMenuBasePredicate.extend({
|
||||||
type: z.literal(ComponentType.RoleSelect),
|
type: z.literal(ComponentType.RoleSelect),
|
||||||
default_values: z
|
default_values: z
|
||||||
.object({ id: z.string(), type: z.literal(SelectMenuDefaultValueType.Role) })
|
.object({ id: snowflakePredicate, type: z.literal(SelectMenuDefaultValueType.Role) })
|
||||||
.array()
|
.array()
|
||||||
.max(25)
|
.max(25)
|
||||||
.optional(),
|
.optional(),
|
||||||
@@ -142,7 +142,7 @@ export const selectMenuStringPredicate = selectMenuBasePredicate
|
|||||||
export const selectMenuUserPredicate = selectMenuBasePredicate.extend({
|
export const selectMenuUserPredicate = selectMenuBasePredicate.extend({
|
||||||
type: z.literal(ComponentType.UserSelect),
|
type: z.literal(ComponentType.UserSelect),
|
||||||
default_values: z
|
default_values: z
|
||||||
.object({ id: z.string(), type: z.literal(SelectMenuDefaultValueType.User) })
|
.object({ id: snowflakePredicate, type: z.literal(SelectMenuDefaultValueType.User) })
|
||||||
.array()
|
.array()
|
||||||
.max(25)
|
.max(25)
|
||||||
.optional(),
|
.optional(),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Buffer } from 'node:buffer';
|
import { Buffer } from 'node:buffer';
|
||||||
import { AllowedMentionsTypes, ComponentType, MessageFlags, MessageReferenceType } from 'discord-api-types/v10';
|
import { AllowedMentionsTypes, ComponentType, MessageFlags, MessageReferenceType } from 'discord-api-types/v10';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
import { snowflakePredicate } from '../Assertions.js';
|
||||||
import { embedPredicate } from './embed/Assertions.js';
|
import { embedPredicate } from './embed/Assertions.js';
|
||||||
import { pollPredicate } from './poll/Assertions.js';
|
import { pollPredicate } from './poll/Assertions.js';
|
||||||
|
|
||||||
@@ -15,7 +16,7 @@ export const rawFilePredicate = z.object({
|
|||||||
|
|
||||||
export const attachmentPredicate = z.object({
|
export const attachmentPredicate = z.object({
|
||||||
// As a string it only makes sense for edits when we do have an attachment snowflake
|
// As a string it only makes sense for edits when we do have an attachment snowflake
|
||||||
id: z.union([z.string(), z.number()]),
|
id: z.union([snowflakePredicate, z.number()]),
|
||||||
description: z.string().max(1_024).optional(),
|
description: z.string().max(1_024).optional(),
|
||||||
duration_secs: z
|
duration_secs: z
|
||||||
.number()
|
.number()
|
||||||
|
|||||||
Reference in New Issue
Block a user