Skip to content

Commit f1aca3d

Browse files
authored
Ensure CreateMessageDto['safeAppId'] is an integer (safe-global#1553)
Increases the strictness of message creation validation to only allow `null`, `0` or positive integers: - Increase strictness of `CreateMessageDtoSchema['safeAppId']` and add appropriate test coverage - Mirror strictness in `createMessageDtoBuilder`
1 parent b2fc902 commit f1aca3d

File tree

3 files changed

+153
-84
lines changed

3 files changed

+153
-84
lines changed

src/routes/messages/entities/__tests__/create-message.dto.builder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ import { CreateMessageDto } from '@/routes/messages/entities/create-message.dto.
55
export function createMessageDtoBuilder(): IBuilder<CreateMessageDto> {
66
return new Builder<CreateMessageDto>()
77
.with('message', faker.word.words({ count: { min: 1, max: 5 } }))
8-
.with('safeAppId', faker.number.int())
8+
.with('safeAppId', faker.number.int({ min: 0 }))
99
.with('signature', faker.string.hexadecimal({ length: 32 }));
1010
}

src/routes/messages/entities/schemas/__tests__/create-message.dto.schema.spec.ts

Lines changed: 151 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -5,98 +5,167 @@ import { faker } from '@faker-js/faker';
55
import { ZodError } from 'zod';
66

77
describe('CreateMessageDtoSchema', () => {
8-
it('should validate a valid record message', () => {
9-
const createMessageDto = createMessageDtoBuilder()
10-
.with('message', JSON.parse(fakeJson()))
11-
.build();
8+
describe('message', () => {
9+
it('should validate a valid record message', () => {
10+
const createMessageDto = createMessageDtoBuilder()
11+
.with('message', JSON.parse(fakeJson()))
12+
.build();
1213

13-
const result = CreateMessageDtoSchema.safeParse(createMessageDto);
14+
const result = CreateMessageDtoSchema.safeParse(createMessageDto);
1415

15-
expect(result.success).toBe(true);
16-
});
16+
expect(result.success).toBe(true);
17+
});
1718

18-
it('should validate a valid string message', () => {
19-
const createMessageDto = createMessageDtoBuilder()
20-
.with('message', faker.word.words())
21-
.build();
19+
it('should validate a valid string message', () => {
20+
const createMessageDto = createMessageDtoBuilder()
21+
.with('message', faker.word.words())
22+
.build();
2223

23-
const result = CreateMessageDtoSchema.safeParse(createMessageDto);
24+
const result = CreateMessageDtoSchema.safeParse(createMessageDto);
2425

25-
expect(result.success).toBe(true);
26-
});
26+
expect(result.success).toBe(true);
27+
});
2728

28-
it('should allow optional safeAppId, defaulting to null', () => {
29-
const createMessageDto = createMessageDtoBuilder().build();
30-
// @ts-expect-error - inferred type doesn't allow optional properties
31-
delete createMessageDto.safeAppId;
29+
it('should not validate without a message', () => {
30+
const createMessageDto = createMessageDtoBuilder().build();
31+
// @ts-expect-error - inferred type doesn't allow optional properties
32+
delete createMessageDto.message;
3233

33-
const result = CreateMessageDtoSchema.safeParse(createMessageDto);
34+
const result = CreateMessageDtoSchema.safeParse(createMessageDto);
3435

35-
expect(result.success && result.data.safeAppId).toBe(null);
36+
expect(!result.success && result.error).toStrictEqual(
37+
new ZodError([
38+
{
39+
code: 'invalid_union',
40+
unionErrors: [
41+
// @ts-expect-error - inferred type doesn't allow optional properties
42+
{
43+
issues: [
44+
{
45+
code: 'invalid_type',
46+
expected: 'object',
47+
received: 'undefined',
48+
path: ['message'],
49+
message: 'Required',
50+
},
51+
],
52+
name: 'ZodError',
53+
},
54+
// @ts-expect-error - inferred type doesn't allow optional properties
55+
{
56+
issues: [
57+
{
58+
code: 'invalid_type',
59+
expected: 'string',
60+
received: 'undefined',
61+
path: ['message'],
62+
message: 'Required',
63+
},
64+
],
65+
name: 'ZodError',
66+
},
67+
],
68+
path: ['message'],
69+
message: 'Invalid input',
70+
},
71+
]),
72+
);
73+
});
3674
});
3775

38-
it('should not validated without a message', () => {
39-
const createMessageDto = createMessageDtoBuilder().build();
40-
// @ts-expect-error - inferred type doesn't allow optional properties
41-
delete createMessageDto.message;
42-
43-
const result = CreateMessageDtoSchema.safeParse(createMessageDto);
44-
45-
expect(!result.success && result.error).toStrictEqual(
46-
new ZodError([
47-
{
48-
code: 'invalid_union',
49-
unionErrors: [
50-
// @ts-expect-error - inferred type doesn't allow optional properties
51-
{
52-
issues: [
53-
{
54-
code: 'invalid_type',
55-
expected: 'object',
56-
received: 'undefined',
57-
path: ['message'],
58-
message: 'Required',
59-
},
60-
],
61-
name: 'ZodError',
62-
},
63-
// @ts-expect-error - inferred type doesn't allow optional properties
64-
{
65-
issues: [
66-
{
67-
code: 'invalid_type',
68-
expected: 'string',
69-
received: 'undefined',
70-
path: ['message'],
71-
message: 'Required',
72-
},
73-
],
74-
name: 'ZodError',
75-
},
76-
],
77-
path: ['message'],
78-
message: 'Invalid input',
79-
},
80-
]),
81-
);
76+
describe('safeAppId', () => {
77+
it('should validate a safeAppId of 0', () => {
78+
const createMessageDto = createMessageDtoBuilder()
79+
.with('safeAppId', 0)
80+
.build();
81+
82+
const result = CreateMessageDtoSchema.safeParse(createMessageDto);
83+
84+
expect(result.success).toBe(true);
85+
});
86+
87+
it('should validate a positive integer safeAppId', () => {
88+
const createMessageDto = createMessageDtoBuilder()
89+
.with('safeAppId', faker.number.int({ min: 1 }))
90+
.build();
91+
92+
const result = CreateMessageDtoSchema.safeParse(createMessageDto);
93+
94+
expect(result.success).toBe(true);
95+
});
96+
97+
it('should not validate a negative safeAppId', () => {
98+
const createMessageDto = createMessageDtoBuilder()
99+
.with('safeAppId', -1)
100+
.build();
101+
102+
const result = CreateMessageDtoSchema.safeParse(createMessageDto);
103+
104+
expect(!result.success && result.error).toStrictEqual(
105+
new ZodError([
106+
{
107+
code: 'too_small',
108+
minimum: 0,
109+
type: 'number',
110+
inclusive: true,
111+
exact: false,
112+
message: 'Number must be greater than or equal to 0',
113+
path: ['safeAppId'],
114+
},
115+
]),
116+
);
117+
});
118+
119+
it('should not validate a float safeAppId', () => {
120+
const createMessageDto = createMessageDtoBuilder()
121+
.with('safeAppId', faker.number.float())
122+
.build();
123+
124+
const result = CreateMessageDtoSchema.safeParse(createMessageDto);
125+
126+
expect(!result.success && result.error).toStrictEqual(
127+
new ZodError([
128+
{
129+
code: 'invalid_type',
130+
expected: 'integer',
131+
received: 'float',
132+
message: 'Expected integer, received float',
133+
path: ['safeAppId'],
134+
},
135+
]),
136+
);
137+
});
138+
139+
it('should validate without safeAppId, defaulting to null', () => {
140+
const createMessageDto = createMessageDtoBuilder().build();
141+
// @ts-expect-error - inferred type doesn't allow optional properties
142+
delete createMessageDto.safeAppId;
143+
144+
const result = CreateMessageDtoSchema.safeParse(createMessageDto);
145+
146+
expect(result.success && result.data.safeAppId).toBe(null);
147+
});
82148
});
83-
it('should not validated without a signature', () => {
84-
const createMessageDto = createMessageDtoBuilder().build();
85-
// @ts-expect-error - inferred type doesn't allow optional properties
86-
delete createMessageDto.signature;
87-
88-
const result = CreateMessageDtoSchema.safeParse(createMessageDto);
89-
90-
expect(!result.success && result.error).toStrictEqual(
91-
new ZodError([
92-
{
93-
code: 'invalid_type',
94-
expected: 'string',
95-
received: 'undefined',
96-
path: ['signature'],
97-
message: 'Required',
98-
},
99-
]),
100-
);
149+
150+
describe('signature', () => {
151+
it('should not validate without a signature', () => {
152+
const createMessageDto = createMessageDtoBuilder().build();
153+
// @ts-expect-error - inferred type doesn't allow optional properties
154+
delete createMessageDto.signature;
155+
156+
const result = CreateMessageDtoSchema.safeParse(createMessageDto);
157+
158+
expect(!result.success && result.error).toStrictEqual(
159+
new ZodError([
160+
{
161+
code: 'invalid_type',
162+
expected: 'string',
163+
received: 'undefined',
164+
path: ['signature'],
165+
message: 'Required',
166+
},
167+
]),
168+
);
169+
});
101170
});
102171
});

src/routes/messages/entities/schemas/create-message.dto.schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ import { z } from 'zod';
22

33
export const CreateMessageDtoSchema = z.object({
44
message: z.union([z.record(z.unknown()), z.string()]),
5-
safeAppId: z.number().nullish().default(null),
5+
safeAppId: z.number().int().gte(0).nullish().default(null),
66
signature: z.string(),
77
});

0 commit comments

Comments
 (0)