improve security against injection (#2669)
* improve security against injection * fix shell injection through ipv4, ipv6 address Co-authored-by: 7megaumka7 <116706490+7megaumka7@users.noreply.github.com> --------- Co-authored-by: 7megaumka7 <116706490+7megaumka7@users.noreply.github.com>
This commit is contained in:
co-authored by
7megaumka7
parent
5f54fa3e58
commit
032347648d
@@ -204,7 +204,9 @@
|
||||
"validBoolean": "{0} must be a valid boolean",
|
||||
"validArray": "{0} must be a valid array",
|
||||
"stringMin": "{0} must be at least {1} Character",
|
||||
"numberMin": "{0} must be at least {1}"
|
||||
"stringMax": "{0} must be at most {1} Character",
|
||||
"numberMin": "{0} must be at least {1}",
|
||||
"numberMax": "{0} must be at most {1}"
|
||||
},
|
||||
"client": {
|
||||
"id": "Client ID",
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
const Verify2faSchema = z.object({
|
||||
totpCode: z.string().min(6).max(6),
|
||||
});
|
||||
import { Verify2faSchema } from '#db/repositories/user/types';
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { totpCode } = await readValidatedBody(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { InferSelectModel } from 'drizzle-orm';
|
||||
import z from 'zod';
|
||||
|
||||
import { isIPv4, isIPv6 } from 'is-ip';
|
||||
import type { client } from './schema';
|
||||
|
||||
export type ClientType = InferSelectModel<typeof client>;
|
||||
@@ -20,7 +21,8 @@ export type UpdateClientType = Omit<
|
||||
const name = z
|
||||
.string({ message: t('zod.client.name') })
|
||||
.min(1, t('zod.client.name'))
|
||||
.pipe(safeStringRefine);
|
||||
.pipe(safeStringRefine)
|
||||
.pipe(controlStringRefine);
|
||||
|
||||
// TODO?: validate iso string
|
||||
const expiresAt = z
|
||||
@@ -32,14 +34,18 @@ const expiresAt = z
|
||||
const address4 = z
|
||||
.string({ message: t('zod.client.address4') })
|
||||
.min(1, { message: t('zod.client.address4') })
|
||||
.pipe(safeStringRefine);
|
||||
.pipe(safeStringRefine)
|
||||
.pipe(controlStringRefine)
|
||||
.refine((v) => isIPv4(v));
|
||||
|
||||
const address6 = z
|
||||
.string({ message: t('zod.client.address6') })
|
||||
.min(1, { message: t('zod.client.address6') })
|
||||
.pipe(safeStringRefine);
|
||||
.pipe(safeStringRefine)
|
||||
.pipe(controlStringRefine)
|
||||
.refine((v) => isIPv6(v));
|
||||
|
||||
const filter = z.string().optional();
|
||||
const filter = z.string().pipe(safeStringRefine).optional();
|
||||
|
||||
const serverAllowedIps = z.array(AddressSchema, {
|
||||
message: t('zod.client.serverAllowedIps'),
|
||||
|
||||
@@ -11,6 +11,7 @@ const metricsEnabled = z.boolean({ message: t('zod.general.metricsEnabled') });
|
||||
const metricsPassword = z
|
||||
.string({ message: t('zod.general.metricsPassword') })
|
||||
.min(1, { message: t('zod.general.metricsPassword') })
|
||||
.pipe(safeStringRefine)
|
||||
.nullable();
|
||||
|
||||
export const GeneralUpdateSchema = z.object({
|
||||
|
||||
@@ -46,9 +46,8 @@ const name = z
|
||||
.pipe(safeStringRefine);
|
||||
|
||||
const email = z
|
||||
.string({ message: t('zod.user.email') })
|
||||
.min(5, t('zod.user.email'))
|
||||
.email({ message: t('zod.user.emailInvalid') })
|
||||
.min(5, t('zod.user.email'))
|
||||
.pipe(safeStringRefine)
|
||||
.nullable();
|
||||
|
||||
@@ -80,3 +79,7 @@ export const UserUpdateTotpSchema = z.union([
|
||||
currentPassword: password,
|
||||
}),
|
||||
]);
|
||||
|
||||
export const Verify2faSchema = z.object({
|
||||
totpCode: totpCode,
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ZodSchema } from 'zod';
|
||||
import type { ZodType } from 'zod';
|
||||
import z from 'zod';
|
||||
import type { H3Event, EventHandlerRequest } from 'h3';
|
||||
import { isIP } from 'is-ip';
|
||||
@@ -20,6 +20,15 @@ export const safeStringRefine = z
|
||||
{ message: t('zod.stringMalformed') }
|
||||
);
|
||||
|
||||
function hasControlChars(str: string) {
|
||||
// eslint-disable-next-line no-control-regex
|
||||
return /[\x00-\x1F\x7F]/.test(str);
|
||||
}
|
||||
|
||||
export const controlStringRefine = z
|
||||
.string()
|
||||
.refine((v) => !hasControlChars(v), { message: t('zod.stringMalformed') });
|
||||
|
||||
export const EnabledSchema = z.boolean({ message: t('zod.enabled') });
|
||||
|
||||
export const MtuSchema = z
|
||||
@@ -70,7 +79,11 @@ export const HSchema = z
|
||||
})
|
||||
.nullable();
|
||||
|
||||
export const ISchema = z.string().nullable();
|
||||
export const ISchema = z
|
||||
.string()
|
||||
.pipe(safeStringRefine)
|
||||
.pipe(controlStringRefine)
|
||||
.nullable();
|
||||
|
||||
export const PortSchema = z
|
||||
.number({ message: t('zod.port') })
|
||||
@@ -85,7 +98,8 @@ export const PersistentKeepaliveSchema = z
|
||||
export const AddressSchema = z
|
||||
.string({ message: t('zod.address') })
|
||||
.min(1, { message: t('zod.address') })
|
||||
.pipe(safeStringRefine);
|
||||
.pipe(safeStringRefine)
|
||||
.pipe(controlStringRefine);
|
||||
|
||||
export const DnsSchema = z.array(AddressSchema, { message: t('zod.dns') });
|
||||
|
||||
@@ -168,7 +182,7 @@ export const schemaForType =
|
||||
};
|
||||
|
||||
export function validateZod<T>(
|
||||
schema: ZodSchema<T>,
|
||||
schema: ZodType<T>,
|
||||
event: H3Event<EventHandlerRequest>
|
||||
) {
|
||||
return async (data: unknown) => {
|
||||
@@ -203,6 +217,22 @@ export function validateZod<T>(
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'too_big':
|
||||
switch (v.origin) {
|
||||
case 'string':
|
||||
newMessage = t('zod.generic.stringMax', [
|
||||
t(v.message),
|
||||
v.maximum,
|
||||
]);
|
||||
break;
|
||||
case 'number':
|
||||
newMessage = t('zod.generic.numberMax', [
|
||||
t(v.message),
|
||||
v.maximum,
|
||||
]);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'invalid_type': {
|
||||
if (v.input === null || v.input === undefined) {
|
||||
newMessage = t('zod.generic.required', [
|
||||
|
||||
Reference in New Issue
Block a user