Skip to content

Commit 955e657

Browse files
authored
fix: handle invalid request path properly in openapi handler (#305)
2 parents 920b13e + bda4a4f commit 955e657

File tree

4 files changed

+101
-21
lines changed

4 files changed

+101
-21
lines changed

packages/server/src/openapi/index.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
} from '@zenstackhq/runtime';
88
import type { ModelZodSchema } from '@zenstackhq/runtime/zod';
99
import { capitalCase } from 'change-case';
10-
import invariant from 'tiny-invariant';
1110
import { fromZodError } from 'zod-validation-error';
1211
import { stripAuxFields } from './utils';
1312

@@ -96,18 +95,15 @@ export async function handleRequest({
9695
logger,
9796
zodSchemas,
9897
}: RequestContext): Promise<Response> {
99-
const parts = path.split('/');
100-
if (parts.length < 2) {
101-
return { status: 400, body: { error: 'invalid request path' } };
102-
}
103-
104-
method = method.toUpperCase();
98+
const parts = path.split('/').filter((p) => !!p);
10599
const op = parts.pop();
106100
const model = parts.pop();
107101

108-
invariant(op);
109-
invariant(model);
102+
if (parts.length !== 0 || !op || !model) {
103+
return { status: 400, body: { message: 'invalid request path' } };
104+
}
110105

106+
method = method.toUpperCase();
111107
const dbOp = op as keyof DbOperations;
112108
let args: unknown;
113109
let resCode = 200;

packages/server/tests/express-adapter.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,21 @@ describe('Express adapter tests', () => {
8787
expect(r.status).toBe(200);
8888
expect(r.body.count).toBe(1);
8989
});
90+
91+
it('invalid path or args', async () => {
92+
const { prisma, zodSchemas } = await loadSchema(schema);
93+
94+
const app = express();
95+
app.use(bodyParser.json());
96+
app.use('/api', ZenStackMiddleware({ getPrisma: () => prisma, zodSchemas }));
97+
98+
let r = await request(app).get('/api/post/');
99+
expect(r.status).toBe(400);
100+
101+
r = await request(app).get('/api/post/findMany/abc');
102+
expect(r.status).toBe(400);
103+
104+
r = await request(app).get('/api/post/findMany?q=abc');
105+
expect(r.status).toBe(400);
106+
});
90107
});

packages/server/tests/fastify-adapter.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,33 @@ describe('Fastify adapter tests', () => {
112112
expect(r.statusCode).toBe(200);
113113
expect(r.json().count).toBe(1);
114114
});
115+
116+
it('invalid path or args', async () => {
117+
const { prisma, zodSchemas } = await loadSchema(schema);
118+
119+
const app = fastify();
120+
app.register(ZenStackFastifyPlugin, {
121+
prefix: '/api',
122+
getPrisma: () => prisma,
123+
zodSchemas,
124+
});
125+
126+
let r = await app.inject({
127+
method: 'GET',
128+
url: '/api/post/',
129+
});
130+
expect(r.statusCode).toBe(400);
131+
132+
r = await app.inject({
133+
method: 'GET',
134+
url: '/api/post/findMany/abc',
135+
});
136+
expect(r.statusCode).toBe(400);
137+
138+
r = await app.inject({
139+
method: 'GET',
140+
url: '/api/post/findMany?q=abc',
141+
});
142+
expect(r.statusCode).toBe(400);
143+
});
115144
});

packages/server/tests/open-api.test.ts

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ describe('OpenAPI server tests', () => {
1111

1212
let r = await handleRequest({
1313
method: 'get',
14-
path: '/api/post/findMany',
14+
path: '/post/findMany',
1515
prisma,
1616
zodSchemas,
1717
});
@@ -20,7 +20,7 @@ describe('OpenAPI server tests', () => {
2020

2121
r = await handleRequest({
2222
method: 'post',
23-
path: '/api/user/create',
23+
path: '/user/create',
2424
query: {},
2525
requestBody: {
2626
include: { posts: true },
@@ -56,7 +56,7 @@ describe('OpenAPI server tests', () => {
5656

5757
r = await handleRequest({
5858
method: 'get',
59-
path: '/api/post/findMany',
59+
path: '/post/findMany',
6060
prisma,
6161
zodSchemas,
6262
});
@@ -65,7 +65,7 @@ describe('OpenAPI server tests', () => {
6565

6666
r = await handleRequest({
6767
method: 'get',
68-
path: '/api/post/findMany',
68+
path: '/post/findMany',
6969
query: { q: JSON.stringify({ where: { viewCount: { gt: 1 } } }) },
7070
prisma,
7171
zodSchemas,
@@ -75,7 +75,7 @@ describe('OpenAPI server tests', () => {
7575

7676
r = await handleRequest({
7777
method: 'put',
78-
path: '/api/user/update',
78+
path: '/user/update',
7979
requestBody: { where: { id: 'user1' }, data: { email: 'user1@def.com' } },
8080
prisma,
8181
zodSchemas,
@@ -85,7 +85,7 @@ describe('OpenAPI server tests', () => {
8585

8686
r = await handleRequest({
8787
method: 'get',
88-
path: '/api/post/count',
88+
path: '/post/count',
8989
query: { q: JSON.stringify({ where: { viewCount: { gt: 1 } } }) },
9090
prisma,
9191
zodSchemas,
@@ -95,7 +95,7 @@ describe('OpenAPI server tests', () => {
9595

9696
r = await handleRequest({
9797
method: 'get',
98-
path: '/api/post/aggregate',
98+
path: '/post/aggregate',
9999
query: { q: JSON.stringify({ _sum: { viewCount: true } }) },
100100
prisma,
101101
zodSchemas,
@@ -105,7 +105,7 @@ describe('OpenAPI server tests', () => {
105105

106106
r = await handleRequest({
107107
method: 'get',
108-
path: '/api/post/groupBy',
108+
path: '/post/groupBy',
109109
query: { q: JSON.stringify({ by: ['published'], _sum: { viewCount: true } }) },
110110
prisma,
111111
zodSchemas,
@@ -120,7 +120,7 @@ describe('OpenAPI server tests', () => {
120120

121121
r = await handleRequest({
122122
method: 'delete',
123-
path: '/api/user/deleteMany',
123+
path: '/user/deleteMany',
124124
query: { q: JSON.stringify({ where: { id: 'user1' } }) },
125125
prisma,
126126
zodSchemas,
@@ -135,14 +135,14 @@ describe('OpenAPI server tests', () => {
135135
// without validation
136136
let r = await handleRequest({
137137
method: 'get',
138-
path: '/api/post/findUnique',
138+
path: '/post/findUnique',
139139
prisma,
140140
});
141141
expect(r.status).toBe(400);
142142

143143
r = await handleRequest({
144144
method: 'get',
145-
path: '/api/post/findUnique',
145+
path: '/post/findUnique',
146146
prisma,
147147
zodSchemas,
148148
});
@@ -152,7 +152,7 @@ describe('OpenAPI server tests', () => {
152152

153153
r = await handleRequest({
154154
method: 'post',
155-
path: '/api/post/create',
155+
path: '/post/create',
156156
requestBody: { data: {} },
157157
prisma,
158158
zodSchemas,
@@ -161,4 +161,42 @@ describe('OpenAPI server tests', () => {
161161
expect((r.body as any).message).toContain('Validation error');
162162
expect((r.body as any).message).toContain('data.title');
163163
});
164+
165+
it('invalid path or args', async () => {
166+
const { prisma } = await loadSchema(schema);
167+
168+
let r = await handleRequest({
169+
method: 'get',
170+
path: '/post/',
171+
prisma,
172+
});
173+
expect(r.status).toBe(400);
174+
expect((r.body as any).message).toContain('invalid request path');
175+
176+
r = await handleRequest({
177+
method: 'get',
178+
path: '/post/findMany/abc',
179+
prisma,
180+
});
181+
expect(r.status).toBe(400);
182+
expect((r.body as any).message).toContain('invalid request path');
183+
184+
r = await handleRequest({
185+
method: 'get',
186+
path: '/post/findUnique',
187+
query: { q: 'abc' },
188+
prisma,
189+
});
190+
expect(r.status).toBe(400);
191+
expect((r.body as any).message).toContain('query param must contain valid JSON');
192+
193+
r = await handleRequest({
194+
method: 'delete',
195+
path: '/post/deleteMany',
196+
query: { q: 'abc' },
197+
prisma,
198+
});
199+
expect(r.status).toBe(400);
200+
expect((r.body as any).message).toContain('query param must contain valid JSON');
201+
});
164202
});

0 commit comments

Comments
 (0)