From 7f318263647053b7803f19085163150210e63377 Mon Sep 17 00:00:00 2001 From: undefined Date: Sun, 3 Dec 2023 13:36:15 +0800 Subject: [PATCH] core: add sanitize-filename --- packages/hydrooj/package.json | 1 + packages/hydrooj/src/handler/contest.ts | 1 - packages/hydrooj/src/handler/problem.ts | 4 ++-- packages/hydrooj/src/lib/validator.ts | 3 ++- packages/hydrooj/src/service/db.ts | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/hydrooj/package.json b/packages/hydrooj/package.json index be9d5ecca..24b48fd4f 100644 --- a/packages/hydrooj/package.json +++ b/packages/hydrooj/package.json @@ -47,6 +47,7 @@ "p-queue": "^7.4.1", "path-to-regexp": "^6.2.1", "require-resolve-hook": "^1.1.0", + "sanitize-filename": "^1.6.3", "saslprep": "^1.0.3", "schemastery": "^3.14.1", "semver": "^7.5.4", diff --git a/packages/hydrooj/src/handler/contest.ts b/packages/hydrooj/src/handler/contest.ts index cbcfc60b1..3cff5d741 100644 --- a/packages/hydrooj/src/handler/contest.ts +++ b/packages/hydrooj/src/handler/contest.ts @@ -641,7 +641,6 @@ export class ContestManagementHandler extends ContestManagementBaseHandler { throw new FileLimitExceededError('size'); } filename ||= file.originalFilename || String.random(16); - if (filename.includes('/') || filename.includes('..')) throw new ValidationError('filename', null, 'Bad filename'); await storage.put(`contest/${domainId}/${tid}/${filename}`, file.filepath, this.user._id); const meta = await storage.getMeta(`contest/${domainId}/${tid}/${filename}`); const payload = { _id: filename, name: filename, ...pick(meta, ['size', 'lastModified', 'etag']) }; diff --git a/packages/hydrooj/src/handler/problem.ts b/packages/hydrooj/src/handler/problem.ts index 8fda52a77..b70fa1262 100644 --- a/packages/hydrooj/src/handler/problem.ts +++ b/packages/hydrooj/src/handler/problem.ts @@ -5,6 +5,7 @@ import { } from 'lodash'; import { Filter, ObjectId } from 'mongodb'; import { nanoid } from 'nanoid'; +import sanitize from 'sanitize-filename'; import parser from '@hydrooj/utils/lib/search'; import { sortFiles, streamToBuffer } from '@hydrooj/utils/lib/utils'; import { @@ -743,7 +744,6 @@ export class ProblemFilesHandler extends ProblemDetailHandler { if (this.pdoc.reference) throw new ProblemIsReferencedError('edit files'); if (!this.request.files.file) throw new ValidationError('file'); filename ||= this.request.files.file.originalFilename || String.random(16); - if (filename.includes('/') || filename.includes('..')) throw new ValidationError('filename', null, 'Bad filename'); if (!this.user.own(this.pdoc, PERM.PERM_EDIT_PROBLEM_SELF)) this.checkPerm(PERM.PERM_EDIT_PROBLEM); const files = []; if (filename.endsWith('.zip') && type === 'testdata') { @@ -758,7 +758,7 @@ export class ProblemFilesHandler extends ProblemDetailHandler { if (!entry.name) continue; files.push({ type, - name: entry.name, + name: sanitize(entry.name), size: entry.header.size, data: () => entry.getData(), }); diff --git a/packages/hydrooj/src/lib/validator.ts b/packages/hydrooj/src/lib/validator.ts index 76863fafc..910b11c6f 100644 --- a/packages/hydrooj/src/lib/validator.ts +++ b/packages/hydrooj/src/lib/validator.ts @@ -3,6 +3,7 @@ import emojiRegex from 'emoji-regex'; import { isSafeInteger } from 'lodash'; import moment from 'moment-timezone'; import { ObjectId } from 'mongodb'; +import sanitize from 'sanitize-filename'; import saslprep from 'saslprep'; type InputType = string | number | Record | any[]; @@ -78,7 +79,7 @@ export const Types: Types = { Key: saslprepString(/^[a-zA-Z0-9-_]+$/), /** @deprecated */ Name: saslprepString(/^.{1,255}$/), - Filename: saslprepString(/^[^\\/?#~!|*]{1,255}$/, (i) => !['con', '.', '..'].includes(i)), + Filename: saslprepString(/^[^\\/?#~!|*]{1,255}$/, (i) => sanitize(i) === i), UidOrName: saslprepString(/^(.{3,31}|[\u4e00-\u9fa5]{2}|-?[0-9]+)$/), Username: saslprepString(/^(.{3,31}|[\u4e00-\u9fa5]{2})$/), Password: basicString(/^.{6,255}$/), diff --git a/packages/hydrooj/src/service/db.ts b/packages/hydrooj/src/service/db.ts index f13d76bfb..abb027b3b 100644 --- a/packages/hydrooj/src/service/db.ts +++ b/packages/hydrooj/src/service/db.ts @@ -1,6 +1,6 @@ /* eslint-disable no-await-in-loop */ import { - Collection, Db, IndexDescription, MongoClient, WriteConcern, + Collection, Db, IndexDescription, MongoClient, } from 'mongodb'; import { Time } from '@hydrooj/utils'; import { Logger } from '../logger';