fix: restrict share uploads to text files
This commit is contained in:
+37
-8
@@ -33,6 +33,21 @@ import {
|
||||
} from "@/lib/share/limits";
|
||||
|
||||
const IMAGE_TYPES = "image/png,image/jpeg,image/webp,image/gif";
|
||||
const TEXT_FILE_TYPES = [
|
||||
"application/json",
|
||||
"application/xml",
|
||||
"application/yaml",
|
||||
"application/toml",
|
||||
"application/javascript",
|
||||
"application/x-javascript",
|
||||
];
|
||||
const TEXT_FILE_ACCEPT = `text/*,${TEXT_FILE_TYPES.join(",")}`;
|
||||
|
||||
function isTextFile(file: File) {
|
||||
return !file.type ||
|
||||
file.type.startsWith("text/") ||
|
||||
TEXT_FILE_TYPES.includes(file.type);
|
||||
}
|
||||
|
||||
function formatBytes(bytes: number) {
|
||||
if (bytes < 1024) return `${bytes} B`;
|
||||
@@ -57,20 +72,33 @@ export function ShareUploadClient() {
|
||||
return () => URL.revokeObjectURL(imagePreview);
|
||||
}, [imagePreview]);
|
||||
|
||||
function showError(message: string) {
|
||||
setError(message);
|
||||
toast.error(message);
|
||||
}
|
||||
|
||||
async function loadTextFile(file: File) {
|
||||
setError(null);
|
||||
if (!isTextFile(file)) {
|
||||
showError("รองรับเฉพาะไฟล์ข้อความเท่านั้น");
|
||||
return;
|
||||
}
|
||||
if (file.size > MAX_SHARE_TEXT_BYTES) {
|
||||
setError("ไฟล์ต้องมีขนาดไม่เกิน 5 MiB");
|
||||
showError("ไฟล์ต้องมีขนาดไม่เกิน 5 MiB");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const content = new TextDecoder("utf-8", { fatal: true }).decode(
|
||||
await file.arrayBuffer()
|
||||
);
|
||||
if (content.includes("\0")) {
|
||||
showError("ไฟล์ข้อความต้องไม่มีอักขระ NUL");
|
||||
return;
|
||||
}
|
||||
setText(content);
|
||||
toast.success(`เปิด ${file.name} แล้ว`);
|
||||
} catch {
|
||||
setError("ไฟล์ต้องเป็นข้อความ UTF-8 ที่ถูกต้อง");
|
||||
showError("ไฟล์ต้องเป็นข้อความ UTF-8 ที่ถูกต้อง");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,11 +106,11 @@ export function ShareUploadClient() {
|
||||
setError(null);
|
||||
if (!file) return;
|
||||
if (!IMAGE_TYPES.split(",").includes(file.type)) {
|
||||
setError("รูปภาพต้องเป็น PNG, JPEG, WebP หรือ GIF");
|
||||
showError("รูปภาพต้องเป็น PNG, JPEG, WebP หรือ GIF");
|
||||
return;
|
||||
}
|
||||
if (file.size > MAX_SHARE_IMAGE_BYTES) {
|
||||
setError("รูปภาพต้องมีขนาดไม่เกิน 50 MiB");
|
||||
showError("รูปภาพต้องมีขนาดไม่เกิน 50 MiB");
|
||||
return;
|
||||
}
|
||||
setImage(file);
|
||||
@@ -93,11 +121,11 @@ export function ShareUploadClient() {
|
||||
event.preventDefault();
|
||||
setError(null);
|
||||
if (!text.trim()) {
|
||||
setError("วางข้อความก่อนสร้างลิงก์");
|
||||
showError("วางข้อความก่อนสร้างลิงก์");
|
||||
return;
|
||||
}
|
||||
if (byteCount > MAX_SHARE_TEXT_BYTES) {
|
||||
setError("ข้อความต้องมีขนาดไม่เกิน 5 MiB");
|
||||
showError("ข้อความต้องมีขนาดไม่เกิน 5 MiB");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -110,12 +138,12 @@ export function ShareUploadClient() {
|
||||
try {
|
||||
const result = await createShareAction(formData);
|
||||
if (!result.success || !result.id) {
|
||||
setError(result.success ? "สร้างลิงก์ไม่สำเร็จ" : result.error);
|
||||
showError(result.success ? "สร้างลิงก์ไม่สำเร็จ" : result.error);
|
||||
return;
|
||||
}
|
||||
router.push(`/share/${result.id}`);
|
||||
} catch {
|
||||
setError("เชื่อมต่อเซิร์ฟเวอร์ไม่สำเร็จ ลองใหม่อีกครั้ง");
|
||||
showError("เชื่อมต่อเซิร์ฟเวอร์ไม่สำเร็จ ลองใหม่อีกครั้ง");
|
||||
} finally {
|
||||
setPending(false);
|
||||
}
|
||||
@@ -196,6 +224,7 @@ export function ShareUploadClient() {
|
||||
<Input
|
||||
ref={textFileRef}
|
||||
type="file"
|
||||
accept={TEXT_FILE_ACCEPT}
|
||||
className="hidden"
|
||||
onChange={(event) => {
|
||||
const file = event.target.files?.[0];
|
||||
|
||||
Reference in New Issue
Block a user