From c72df2b8a5abb2ea24eeb419e9aeff658e3b1933 Mon Sep 17 00:00:00 2001 From: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Sun, 19 Apr 2026 01:56:12 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=E3=82=A8=E3=83=B3=E3=83=89=E3=83=9D?= =?UTF-8?q?=E3=82=A4=E3=83=B3=E3=83=88=E3=81=AB=E3=81=BE=E3=81=A4=E3=82=8F?= =?UTF-8?q?=E3=82=8B=E3=83=86=E3=82=B9=E3=83=88=E3=82=92unit=E3=81=8B?= =?UTF-8?q?=E3=82=89e2e=E3=81=AB=E7=A7=BB=E5=8B=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/test/e2e/endpoints.ts | 124 +++++++++- .../unit/server/api/drive/files/create.ts | 217 ------------------ 2 files changed, 123 insertions(+), 218 deletions(-) delete mode 100644 packages/backend/test/unit/server/api/drive/files/create.ts diff --git a/packages/backend/test/e2e/endpoints.ts b/packages/backend/test/e2e/endpoints.ts index 274a6b6b27..09198384c4 100644 --- a/packages/backend/test/e2e/endpoints.ts +++ b/packages/backend/test/e2e/endpoints.ts @@ -10,7 +10,7 @@ import { describe, beforeAll, test, expect } from 'vitest'; // node-fetch only supports it's own Blob yet // https://github.com/node-fetch/node-fetch/pull/1664 import { Blob } from 'node-fetch'; -import { api, castAsError, initTestDb, post, signup, simpleGet, uploadFile } from '../utils.js'; +import { api, castAsError, initTestDb, post, role, signup, simpleGet, uploadFile } from '../utils.js'; import type * as misskey from 'misskey-js'; import { MiUser } from '@/models/_.js'; @@ -582,6 +582,30 @@ describe('Endpoints', () => { }); describe('drive/files/create', () => { + const assignRole = async (userId: string, policies: Record) => { + const createdRole = await role(alice, {}, policies); + + const assign = await api('admin/roles/assign', { + userId, + roleId: createdRole.id, + }, alice); + + assert.strictEqual(assign.status, 204); + + return createdRole; + }; + + const cleanupRole = async (userId: string, roleId: string) => { + await api('admin/roles/unassign', { + userId, + roleId, + }, alice); + + await api('admin/roles/delete', { + roleId, + }, alice); + }; + test('ファイルを作成できる', async () => { const res = await uploadFile(alice); @@ -660,6 +684,104 @@ describe('Endpoints', () => { assert.strictEqual(webpublicType, 'image/webp'); }); } + + test('uploadableFileTypes が */* なら任意のファイルをアップロードできる', async () => { + const createdRole = await assignRole(bob.id, { + uploadableFileTypes: { + useDefault: false, + priority: 1, + value: ['*/*'], + }, + }); + + try { + const res = await uploadFile(bob, { + blob: new Blob([new Uint8Array(10)]), + }); + + assert.strictEqual(res.status, 200); + } finally { + await cleanupRole(bob.id, createdRole.id); + } + }); + + test('uploadableFileTypes に含まれない MIME type は拒否される', async () => { + const createdRole = await assignRole(bob.id, { + uploadableFileTypes: { + useDefault: false, + priority: 1, + value: ['image/png'], + }, + }); + + try { + const res = await uploadFile(bob, { path: '192.jpg' }); + + assert.strictEqual(res.status, 400); + assert.ok(res.body); + assert.strictEqual(castAsError(res.body).error.code, 'UNALLOWED_FILE_TYPE'); + } finally { + await cleanupRole(bob.id, createdRole.id); + } + }); + + test('maxFileSizeMb 制限付きロールでも制限内ならアップロードできる', async () => { + const allowAllTypesRole = await assignRole(bob.id, { + uploadableFileTypes: { + useDefault: false, + priority: 1, + value: ['*/*'], + }, + }); + const tinyAttachmentRole = await assignRole(bob.id, { + maxFileSizeMb: { + useDefault: false, + priority: 1, + value: 10 / 1024 / 1024, // 10バイト + }, + }); + + try { + const res = await uploadFile(bob, { + blob: new Blob([new Uint8Array(10)]), + }); + + assert.strictEqual(res.status, 200); + } finally { + await cleanupRole(bob.id, tinyAttachmentRole.id); + await cleanupRole(bob.id, allowAllTypesRole.id); + } + }); + + test('maxFileSizeMb 制限を超えると 413 になる', async () => { + const allowAllTypesRole = await assignRole(bob.id, { + uploadableFileTypes: { + useDefault: false, + priority: 1, + value: ['*/*'], + }, + }); + const tinyAttachmentRole = await assignRole(bob.id, { + maxFileSizeMb: { + useDefault: false, + priority: 1, + value: 10 / 1024 / 1024, // 10バイト + }, + }); + + try { + const res = await uploadFile(bob, { + blob: new Blob([new Uint8Array(11)]), + }); + + assert.strictEqual(res.status, 413); + assert.ok(res.body); + assert.strictEqual(castAsError(res.body).error.code, 'MAX_FILE_SIZE_EXCEEDED'); + } finally { + await cleanupRole(bob.id, tinyAttachmentRole.id); + await cleanupRole(bob.id, allowAllTypesRole.id); + } + }); }); describe('drive/files/update', () => { diff --git a/packages/backend/test/unit/server/api/drive/files/create.ts b/packages/backend/test/unit/server/api/drive/files/create.ts deleted file mode 100644 index 5a97f1db89..0000000000 --- a/packages/backend/test/unit/server/api/drive/files/create.ts +++ /dev/null @@ -1,217 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { describe, expect, beforeAll, afterAll, beforeEach, test } from 'vitest'; -import { Test, TestingModule } from '@nestjs/testing'; -import { FastifyInstance } from 'fastify'; -import request from 'supertest'; -import { randomString } from '../../../../../utils.js'; -import { CoreModule } from '@/core/CoreModule.js'; -import { RoleService } from '@/core/RoleService.js'; -import { DI } from '@/di-symbols.js'; -import { GlobalModule } from '@/GlobalModule.js'; -import { DriveFoldersRepository, MiDriveFolder, MiRole, UserProfilesRepository, UsersRepository } from '@/models/_.js'; -import { MiUser } from '@/models/User.js'; -import { ServerModule } from '@/server/ServerModule.js'; -import { ServerService } from '@/server/ServerService.js'; -import { IdService } from '@/core/IdService.js'; - -// TODO: uploadableFileTypes で許可されていないファイルが弾かれるかのテスト - -describe('/drive/files/create', () => { - let module: TestingModule; - let server: FastifyInstance; - let roleService: RoleService; - let idService: IdService; - - let root: MiUser; - let role_tinyAttachment: MiRole; - let role_imageOnly: MiRole; - let role_allowAllTypes: MiRole; - - let folder: MiDriveFolder; - - beforeAll(async () => { - module = await Test.createTestingModule({ - imports: [GlobalModule, CoreModule, ServerModule], - }).compile(); - module.enableShutdownHooks(); - - const serverService = module.get(ServerService); - await serverService.launch(); - server = serverService.fastify; - - idService = module.get(IdService); - - const usersRepository = module.get(DI.usersRepository); - await usersRepository.createQueryBuilder().delete().execute(); - root = await usersRepository.insert({ - id: idService.gen(), - username: 'root', - usernameLower: 'root', - token: '1234567890123456', - }).then(x => usersRepository.findOneByOrFail(x.identifiers[0])); - - const userProfilesRepository = module.get(DI.userProfilesRepository); - await userProfilesRepository.createQueryBuilder().delete().execute(); - await userProfilesRepository.insert({ - userId: root.id, - }); - - const driveFoldersRepository = module.get(DI.driveFoldersRepository); - folder = await driveFoldersRepository.insertOne({ - id: idService.gen(), - name: 'root-folder', - parentId: null, - userId: root.id, - }); - - roleService = module.get(RoleService); - role_imageOnly = await roleService.create({ - name: 'test-role001', - description: 'Test role001 description', - target: 'manual', - policies: { - uploadableFileTypes: { - useDefault: false, - priority: 1, - value: ['image/png'], - }, - }, - }); - role_allowAllTypes = await roleService.create({ - name: 'test-role002', - description: 'Test role002 description', - target: 'manual', - policies: { - uploadableFileTypes: { - useDefault: false, - priority: 1, - value: ['*/*'], - }, - }, - }); - role_tinyAttachment = await roleService.create({ - name: 'test-role003', - description: 'Test role003 description', - target: 'manual', - policies: { - maxFileSizeMb: { - useDefault: false, - priority: 1, - // 10byte - value: 10 / 1024 / 1024, - }, - }, - }); - }); - - beforeEach(async () => { - await roleService.unassign(root.id, role_tinyAttachment.id).catch(() => { - }); - await roleService.unassign(root.id, role_imageOnly.id).catch(() => { - }); - await roleService.unassign(root.id, role_allowAllTypes.id).catch(() => { - }); - }); - - afterAll(async () => { - await server.close(); - await module.close(); - }); - - async function postFile(props: { - name: string, - comment: string, - isSensitive: boolean, - force: boolean, - fileContent: Buffer | string, - }) { - const { name, comment, isSensitive, force, fileContent } = props; - - return await request(server.server) - .post('/api/drive/files/create') - .set('Content-Type', 'multipart/form-data') - .attach('file', fileContent) - .field('name', name) - .field('comment', comment) - .field('isSensitive', isSensitive) - .field('force', force) - .field('folderId', folder.id) - .field('i', root.token ?? ''); - } - - test('200 ok (all types allowed)', async () => { - await roleService.assign(root.id, role_allowAllTypes.id); - - const name = randomString(); - const comment = randomString(); - const result = await postFile({ - name: name, - comment: comment, - isSensitive: true, - force: true, - fileContent: Buffer.from('a'.repeat(1000 * 1000)), - }); - expect(result.statusCode).toBe(200); - expect(result.body.name).toBe(name + '.unknown'); - expect(result.body.comment).toBe(comment); - expect(result.body.isSensitive).toBe(true); - expect(result.body.folderId).toBe(folder.id); - }); - - test('400 when not allowed type', async () => { - await roleService.assign(root.id, role_imageOnly.id); - - const name = randomString(); - const comment = randomString(); - const result = await postFile({ - name: name, - comment: comment, - isSensitive: true, - force: true, - fileContent: Buffer.from('a'.repeat(10)), - }); - expect(result.statusCode).toBe(400); - expect(result.body.error.code).toBe('UNALLOWED_FILE_TYPE'); - }); - - test('200 ok (with size limited role)', async () => { - await roleService.assign(root.id, role_allowAllTypes.id); - await roleService.assign(root.id, role_tinyAttachment.id); - - const name = randomString(); - const comment = randomString(); - const result = await postFile({ - name: name, - comment: comment, - isSensitive: true, - force: true, - fileContent: Buffer.from('a'.repeat(10)), - }); - expect(result.statusCode).toBe(200); - expect(result.body.name).toBe(name + '.unknown'); - expect(result.body.comment).toBe(comment); - expect(result.body.isSensitive).toBe(true); - expect(result.body.folderId).toBe(folder.id); - }); - - test('413 too large', async () => { - await roleService.assign(root.id, role_allowAllTypes.id); - await roleService.assign(root.id, role_tinyAttachment.id); - - const name = randomString(); - const comment = randomString(); - const result = await postFile({ - name: name, - comment: comment, - isSensitive: true, - force: true, - fileContent: Buffer.from('a'.repeat(11)), - }); - expect(result.statusCode).toBe(413); - expect(result.body.error.code).toBe('MAX_FILE_SIZE_EXCEEDED'); - }); -});