Skip to content

Commit

Permalink
fix: missing basic validation in route params (#213)
Browse files Browse the repository at this point in the history
Merge pull request #213 from ckb-cell/fix/210-empty-param-validation
  • Loading branch information
Flouse authored Aug 29, 2024
2 parents cb25b92 + 57c304e commit 38224cc
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 26 deletions.
27 changes: 24 additions & 3 deletions src/app.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import fastify from 'fastify';
import fastify, { type FastifySchemaCompiler } from 'fastify';
import { FastifyInstance } from 'fastify';
import sensible from '@fastify/sensible';
import sensible, { httpErrors } from '@fastify/sensible';
import compress from '@fastify/compress';
import bitcoinRoutes from './routes/bitcoin';
import tokenRoutes from './routes/token';
Expand All @@ -12,7 +12,7 @@ import { getSafeEnvs } from './env';
import container from './container';
import { asValue } from 'awilix';
import options from './options';
import { serializerCompiler, validatorCompiler, ZodTypeProvider } from 'fastify-type-provider-zod';
import { serializerCompiler, ZodTypeProvider } from 'fastify-type-provider-zod';
import cors from './plugins/cors';
import { NetworkType } from './constants';
import rgbppRoutes from './routes/rgbpp';
Expand All @@ -23,6 +23,7 @@ import internalRoutes from './routes/internal';
import healthcheck from './plugins/healthcheck';
import sentry from './plugins/sentry';
import cron from './plugins/cron';
import { ZodAny } from 'zod';

async function routes(fastify: FastifyInstance) {
fastify.log.info(`Process env: ${JSON.stringify(getSafeEnvs(), null, 2)}`);
Expand Down Expand Up @@ -59,6 +60,26 @@ async function routes(fastify: FastifyInstance) {
}
}

export const validatorCompiler: FastifySchemaCompiler<ZodAny> =
({ schema }) =>
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(data) => {
const result = schema.safeParse(data);
if (result.success) {
return { value: result.data };
}

const error = result.error;
if (error.errors.length) {
const firstError = error.errors[0];
const propName = firstError.path.length ? firstError.path.join('.') : 'param';
return {
error: httpErrors.badRequest(`Invalid ${propName}: ${error.errors[0].message}`),
};
}
return { error };
};

export function buildFastify() {
const app = fastify(options).withTypeProvider<ZodTypeProvider>();
app.setValidatorCompiler(validatorCompiler);
Expand Down
12 changes: 8 additions & 4 deletions src/routes/bitcoin/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const blockRoutes: FastifyPluginCallback<Record<never, never>, Server, ZodTypePr
description: 'Get a block by its hash',
tags: ['Bitcoin'],
params: z.object({
hash: z.string().describe('The Bitcoin block hash'),
hash: z.string().length(64, 'should be a 64-character hex string').describe('The Bitcoin block hash'),
}),
response: {
200: Block,
Expand All @@ -35,7 +35,7 @@ const blockRoutes: FastifyPluginCallback<Record<never, never>, Server, ZodTypePr
description: 'Get block transaction ids by its hash',
tags: ['Bitcoin'],
params: z.object({
hash: z.string().describe('The Bitcoin block hash'),
hash: z.string().length(64, 'should be a 64-character hex string').describe('The Bitcoin block hash'),
}),
response: {
200: z.object({
Expand All @@ -59,7 +59,7 @@ const blockRoutes: FastifyPluginCallback<Record<never, never>, Server, ZodTypePr
description: 'Get a block header by its hash',
tags: ['Bitcoin'],
params: z.object({
hash: z.string().describe('The Bitcoin block hash'),
hash: z.string().length(64, 'should be a 64-character hex string').describe('The Bitcoin block hash'),
}),
response: {
200: z.object({
Expand All @@ -85,7 +85,11 @@ const blockRoutes: FastifyPluginCallback<Record<never, never>, Server, ZodTypePr
description: 'Get a block hash by its height',
tags: ['Bitcoin'],
params: z.object({
height: z.coerce.number().describe('The Bitcoin block height'),
height: z
.string()
.min(1, 'cannot be empty')
.pipe(z.coerce.number().min(0, 'cannot be negative'))
.describe('The Bitcoin block height'),
}),
response: {
200: z.object({
Expand Down
4 changes: 2 additions & 2 deletions src/routes/bitcoin/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const transactionRoutes: FastifyPluginCallback<Record<never, never>, Server, Zod
description: 'Get a transaction by its txid',
tags: ['Bitcoin'],
params: z.object({
txid: z.string().describe('The Bitcoin transaction id'),
txid: z.string().length(64, 'should be a 64-character hex string').describe('The Bitcoin transaction id'),
}),
response: {
200: Transaction,
Expand All @@ -62,7 +62,7 @@ const transactionRoutes: FastifyPluginCallback<Record<never, never>, Server, Zod
description: 'Get a transaction hex by its txid',
tags: ['Bitcoin'],
params: z.object({
txid: z.string().describe('The Bitcoin transaction id'),
txid: z.string().length(64, 'should be a 64-character hex string').describe('The Bitcoin transaction id'),
}),
response: {
200: z.object({
Expand Down
6 changes: 3 additions & 3 deletions src/routes/rgbpp/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const assetsRoute: FastifyPluginCallback<Record<never, never>, Server, ZodTypePr
description: `Get RGB++ assets by BTC txid.`,
tags: ['RGB++'],
params: z.object({
btc_txid: z.string(),
btc_txid: z.string().length(64, 'Should be a 64-character hex string'),
}),
response: {
200: z.array(
Expand Down Expand Up @@ -65,8 +65,8 @@ const assetsRoute: FastifyPluginCallback<Record<never, never>, Server, ZodTypePr
description: 'Get RGB++ assets by btc txid and vout',
tags: ['RGB++'],
params: z.object({
btc_txid: z.string(),
vout: z.coerce.number(),
btc_txid: z.string().length(64, 'should be a 64-character hex string'),
vout: z.string().min(1, 'cannot be empty').pipe(z.coerce.number().min(0, 'cannot be negative')),
}),
response: {
200: z.array(
Expand Down
8 changes: 5 additions & 3 deletions src/routes/rgbpp/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ const transactionRoute: FastifyPluginCallback<Record<never, never>, Server, ZodT
}
const parsed = CKBVirtualResult.safeParse(value);
if (!parsed.success) {
throw new Error(`Invalid CKB virtual result: ${JSON.stringify(parsed.error.flatten())}`);
throw fastify.httpErrors.badRequest(
`Invalid CKB virtual result: ${JSON.stringify(parsed.error.flatten())}`,
);
}
return parsed.data;
}),
Expand Down Expand Up @@ -54,7 +56,7 @@ const transactionRoute: FastifyPluginCallback<Record<never, never>, Server, ZodT
description: `Get the CKB transaction hash by BTC txid.`,
tags: ['RGB++'],
params: z.object({
btc_txid: z.string(),
btc_txid: z.string().length(64, 'should be a 64-character hex string'),
}),
response: {
200: z.object({
Expand Down Expand Up @@ -116,7 +118,7 @@ const transactionRoute: FastifyPluginCallback<Record<never, never>, Server, ZodT
`,
tags: ['RGB++'],
params: z.object({
btc_txid: z.string(),
btc_txid: z.string().length(64, 'should be a 64-character hex string'),
}),
querystring: z.object({
with_data: z.enum(['true', 'false']).default('false'),
Expand Down
12 changes: 1 addition & 11 deletions test/routes/__snapshots__/token.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`\`/token/generate\` - without params 1`] = `
"[
{
"code": "invalid_type",
"expected": "object",
"received": "null",
"path": [],
"message": "Expected object, received null"
}
]"
`;
exports[`\`/token/generate\` - without params 1`] = `"Invalid param: Expected object, received null"`;

0 comments on commit 38224cc

Please sign in to comment.