mirror of
https://github.com/kyantech/Palmr.git
synced 2025-10-23 06:11:58 +00:00
feat: enhance filename encoding for Content-Disposition header
- Implemented a new method to safely encode filenames for the Content-Disposition header in both FilesystemController and S3StorageProvider, improving file download handling. - Updated the upload and presigned URL generation processes to utilize the new encoding method, ensuring proper filename formatting and security. - Enhanced validation logic in FilesystemStorageProvider for download tokens to improve robustness.
This commit is contained in:
@@ -8,6 +8,50 @@ import { pipeline } from "stream/promises";
|
||||
export class FilesystemController {
|
||||
private fileService = new FileService();
|
||||
|
||||
/**
|
||||
* Safely encode filename for Content-Disposition header
|
||||
*/
|
||||
private encodeFilenameForHeader(filename: string): string {
|
||||
if (!filename || filename.trim() === "") {
|
||||
return 'attachment; filename="download"';
|
||||
}
|
||||
|
||||
let sanitized = filename
|
||||
.replace(/"/g, "'")
|
||||
.replace(/[\r\n\t\v\f]/g, "")
|
||||
.replace(/[\\|/]/g, "-")
|
||||
.replace(/[<>:|*?]/g, "");
|
||||
|
||||
sanitized = sanitized
|
||||
.split("")
|
||||
.filter((char) => {
|
||||
const code = char.charCodeAt(0);
|
||||
return code >= 32 && !(code >= 127 && code <= 159);
|
||||
})
|
||||
.join("")
|
||||
.trim();
|
||||
|
||||
if (!sanitized) {
|
||||
return 'attachment; filename="download"';
|
||||
}
|
||||
|
||||
const asciiSafe = sanitized
|
||||
.split("")
|
||||
.filter((char) => {
|
||||
const code = char.charCodeAt(0);
|
||||
return code >= 32 && code <= 126;
|
||||
})
|
||||
.join("");
|
||||
|
||||
if (asciiSafe && asciiSafe.trim()) {
|
||||
const encoded = encodeURIComponent(sanitized);
|
||||
return `attachment; filename="${asciiSafe}"; filename*=UTF-8''${encoded}`;
|
||||
} else {
|
||||
const encoded = encodeURIComponent(sanitized);
|
||||
return `attachment; filename*=UTF-8''${encoded}`;
|
||||
}
|
||||
}
|
||||
|
||||
async upload(request: FastifyRequest, reply: FastifyReply) {
|
||||
try {
|
||||
const { token } = request.params as { token: string };
|
||||
@@ -100,6 +144,7 @@ export class FilesystemController {
|
||||
const provider = FilesystemStorageProvider.getInstance();
|
||||
|
||||
const tokenData = provider.validateDownloadToken(token);
|
||||
|
||||
if (!tokenData) {
|
||||
return reply.status(400).send({ error: "Invalid or expired download token" });
|
||||
}
|
||||
@@ -109,7 +154,7 @@ export class FilesystemController {
|
||||
const isLargeFile = stats.size > 50 * 1024 * 1024;
|
||||
|
||||
const fileName = tokenData.fileName || "download";
|
||||
reply.header("Content-Disposition", `attachment; filename="${fileName}"`);
|
||||
reply.header("Content-Disposition", this.encodeFilenameForHeader(fileName));
|
||||
reply.header("Content-Type", "application/octet-stream");
|
||||
reply.header("Content-Length", stats.size);
|
||||
|
||||
|
Reference in New Issue
Block a user