fix: restrict share uploads to text files

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