From 149c3671f7495099d5eb7026278d54ea867e9508 Mon Sep 17 00:00:00 2001 From: undefined Date: Sun, 29 Jan 2023 11:06:16 +0800 Subject: [PATCH] core: add avatar validation --- packages/hydrooj/src/handler/home.ts | 8 +++++--- packages/hydrooj/src/lib/avatar.ts | 20 ++++++++++++++++---- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/packages/hydrooj/src/handler/home.ts b/packages/hydrooj/src/handler/home.ts index 6ddb05555..d149151b9 100644 --- a/packages/hydrooj/src/handler/home.ts +++ b/packages/hydrooj/src/handler/home.ts @@ -10,7 +10,7 @@ import { UserNotFoundError, ValidationError, VerifyPasswordError, } from '../error'; import { DomainDoc, MessageDoc, Setting } from '../interface'; -import avatar from '../lib/avatar'; +import avatar, { validate } from '../lib/avatar'; import * as mail from '../lib/mail'; import * as useragent from '../lib/useragent'; import { verifyTFA } from '../lib/verifyTFA'; @@ -373,8 +373,10 @@ class HomeSettingsHandler extends Handler { class HomeAvatarHandler extends Handler { @param('avatar', Types.String, true) async post(domainId: string, input: string) { - if (input) await user.setById(this.user._id, { avatar: input }); - else if (this.request.files.file) { + if (input) { + if (!validate(input)) throw new ValidationError('avatar'); + await user.setById(this.user._id, { avatar: input }); + } else if (this.request.files.file) { const file = this.request.files.file; if (file.size > 8 * 1024 * 1024) throw new ValidationError('file'); const ext = path.extname(file.originalFilename); diff --git a/packages/hydrooj/src/lib/avatar.ts b/packages/hydrooj/src/lib/avatar.ts index 887adf341..55fd76ff1 100644 --- a/packages/hydrooj/src/lib/avatar.ts +++ b/packages/hydrooj/src/lib/avatar.ts @@ -1,10 +1,11 @@ +import { Types } from '../service/decorators'; import { md5 } from '../utils'; type AvatarProvider = (src: string, size: number) => string; export const providers: Record = { gravatar: (email, size) => `${global.Hydro.model.system.get('avatar.gravatar_url')}` - + `${md5((email || '').toString().trim().toLowerCase())}?d=mm&s=${size || 32}`, + + `${md5((email || '').toString().trim().toLowerCase())}?d=mm&s=${size || 32}`, qq: (id) => `//q1.qlogo.cn/g?b=qq&nk=${(/(\d+)/g.exec(id) || ['', ''])[1]}&s=160`, github: (id, size) => `//github.com/${id}.png?size=${Math.min(size, 460)}`, url: (url) => url, @@ -13,13 +14,24 @@ export const providers: Record = { function avatar(src: string, size = 64, fallback = '') { src ||= fallback; let index = src.indexOf(':'); - if (index === -1) src = fallback; - index = src.indexOf(':'); + if (index === -1 && fallback) { + src = fallback; + index = src.indexOf(':'); + } if (index === -1) return providers.gravatar('', size); - const [provider, str] = [src.substr(0, index), src.substr(index + 1, src.length)]; + const [provider, str] = [src.substring(0, index), src.substring(index + 1, src.length)]; if (!providers[provider] || !str) return providers.gravatar('', size); return providers[provider](str, size); } +export function validate(input: string) { + if (!input) return true; + if (input.startsWith('url:')) return true; + if (input.startsWith('github:')) return /^[a-zA-Z0-9-]+$/.test(input.substring(7, input.length)); + if (input.startsWith('qq:')) return /^[1-9]\d{4,}$/.test(input.substring(3)); + if (input.startsWith('gravatar:')) return Types.Email[1](input.substring(9)); + return false; +} + export default avatar; global.Hydro.lib.avatar = avatar;