fix: code review cleanup

- Add missing dependencies: ioredis, adm-zip, archiver, gray-matter, @types/multer, @types/archiver
- Fix import statements: use default imports for AdmZip, archiver, gray-matter
- Remove unused imports: ArrayMinSize
- Fix export types: use 'export type' for type-only exports
- Replace 'any' types with proper types:
  - AuthUser for user parameters
  - ExportEntry interface for entry data
  - unknown for frontmatter parsing parameters
  - Record<string, unknown> for dynamic objects
- Add security improvements:
  - File upload size limit: 50MB max
  - File type validation in FileInterceptor
  - Path traversal protection in zip extraction
  - Zip bomb protection: max 1000 files, 100MB uncompressed
- Fix exactOptionalPropertyTypes issues: use conditional spreading for optional fields
This commit is contained in:
Jason Woltje
2026-01-30 00:15:44 -06:00
parent c4c15ee87e
commit 10a812aedc
6 changed files with 463 additions and 35 deletions

View File

@@ -17,6 +17,7 @@ import { AuthGuard } from "../auth/guards/auth.guard";
import { WorkspaceGuard, PermissionGuard } from "../common/guards";
import { Workspace, Permission, RequirePermission } from "../common/decorators";
import { CurrentUser } from "../auth/decorators/current-user.decorator";
import type { AuthUser } from "../auth/types/better-auth-request.interface";
/**
* Controller for knowledge import/export endpoints
@@ -34,10 +35,42 @@ export class ImportExportController {
*/
@Post("import")
@RequirePermission(Permission.WORKSPACE_MEMBER)
@UseInterceptors(FileInterceptor("file"))
@UseInterceptors(
FileInterceptor("file", {
limits: {
fileSize: 50 * 1024 * 1024, // 50MB max file size
},
fileFilter: (_req, file, callback) => {
// Only accept .md and .zip files
const allowedMimeTypes = [
"text/markdown",
"application/zip",
"application/x-zip-compressed",
];
const allowedExtensions = [".md", ".zip"];
const fileExtension = file.originalname.toLowerCase().slice(
file.originalname.lastIndexOf(".")
);
if (
allowedMimeTypes.includes(file.mimetype) ||
allowedExtensions.includes(fileExtension)
) {
callback(null, true);
} else {
callback(
new BadRequestException(
"Invalid file type. Only .md and .zip files are accepted."
),
false
);
}
},
})
)
async importEntries(
@Workspace() workspaceId: string,
@CurrentUser() user: any,
@CurrentUser() user: AuthUser,
@UploadedFile() file: Express.Multer.File
): Promise<ImportResponseDto> {
if (!file) {