Restore the community photo after reload and reject empty, oversized, SVG, and spoofed uploads.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -14,13 +14,40 @@ import { saveCreateFlowUpload } from "../../../lib/server/uploads/saveCreateFlow
|
||||
import { getUploadRootFromEnv } from "../../../lib/server/uploads/uploadRoot";
|
||||
import {
|
||||
CREATE_FLOW_UPLOAD_MAX_BYTES,
|
||||
maxBytesForPurpose,
|
||||
type CreateFlowUploadPurpose,
|
||||
} from "../../../lib/server/uploads/uploadConstants";
|
||||
import type { CreateFlowUploadValidationReason } from "../../../lib/create/createFlowUploadValidation";
|
||||
|
||||
function asUploadedBlob(value: FormDataEntryValue | null): Blob | null {
|
||||
if (typeof value !== "object" || value === null) return null;
|
||||
const candidate = value as Partial<Blob>;
|
||||
if (typeof candidate.arrayBuffer !== "function") return null;
|
||||
if (typeof candidate.size !== "number") return null;
|
||||
return value as Blob;
|
||||
}
|
||||
|
||||
function isPurpose(x: string): x is CreateFlowUploadPurpose {
|
||||
return x === "communityAvatar" || x === "customMethodAttachment";
|
||||
}
|
||||
|
||||
function messageForValidationReason(
|
||||
reason: CreateFlowUploadValidationReason,
|
||||
): string {
|
||||
switch (reason) {
|
||||
case "empty":
|
||||
return "File is empty.";
|
||||
case "tooLarge":
|
||||
return "File exceeds the maximum allowed size for this upload purpose.";
|
||||
case "svg":
|
||||
return "SVG uploads are not allowed.";
|
||||
case "undecodable":
|
||||
return "File could not be decoded as a valid image.";
|
||||
case "invalidType":
|
||||
return "File type is not allowed for this upload purpose.";
|
||||
}
|
||||
}
|
||||
|
||||
export const POST = apiRoute("uploads.post", async (request: NextRequest) => {
|
||||
if (!isDatabaseConfigured()) {
|
||||
return dbUnavailable();
|
||||
@@ -54,7 +81,7 @@ export const POST = apiRoute("uploads.post", async (request: NextRequest) => {
|
||||
}
|
||||
|
||||
const purposeRaw = formData.get("purpose");
|
||||
const file = formData.get("file");
|
||||
const file = asUploadedBlob(formData.get("file"));
|
||||
|
||||
if (typeof purposeRaw !== "string" || !isPurpose(purposeRaw)) {
|
||||
return errorJson(
|
||||
@@ -64,7 +91,7 @@ export const POST = apiRoute("uploads.post", async (request: NextRequest) => {
|
||||
);
|
||||
}
|
||||
|
||||
if (!(file instanceof File)) {
|
||||
if (!file) {
|
||||
return errorJson(
|
||||
"validation_error",
|
||||
"Missing `file` field (multipart file).",
|
||||
@@ -72,21 +99,29 @@ export const POST = apiRoute("uploads.post", async (request: NextRequest) => {
|
||||
);
|
||||
}
|
||||
|
||||
if (file.size > CREATE_FLOW_UPLOAD_MAX_BYTES) {
|
||||
if (file.size === 0) {
|
||||
return errorJson("validation_error", messageForValidationReason("empty"), 400, {
|
||||
details: { reason: "empty" },
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
file.size > CREATE_FLOW_UPLOAD_MAX_BYTES ||
|
||||
file.size > maxBytesForPurpose(purposeRaw)
|
||||
) {
|
||||
return errorJson(
|
||||
"payload_too_large",
|
||||
`File exceeds maximum allowed size (${CREATE_FLOW_UPLOAD_MAX_BYTES} bytes).`,
|
||||
messageForValidationReason("tooLarge"),
|
||||
413,
|
||||
{ details: { reason: "tooLarge" } },
|
||||
);
|
||||
}
|
||||
|
||||
const buf = Buffer.from(await file.arrayBuffer());
|
||||
const mimeType = file.type || "application/octet-stream";
|
||||
|
||||
const saved = await saveCreateFlowUpload({
|
||||
purpose: purposeRaw,
|
||||
buffer: buf,
|
||||
mimeType,
|
||||
});
|
||||
|
||||
if ("error" in saved) {
|
||||
@@ -95,10 +130,20 @@ export const POST = apiRoute("uploads.post", async (request: NextRequest) => {
|
||||
"File uploads are not configured (UPLOAD_ROOT is unset).",
|
||||
);
|
||||
}
|
||||
const reason = saved.reason ?? "invalidType";
|
||||
if (reason === "tooLarge") {
|
||||
return errorJson(
|
||||
"payload_too_large",
|
||||
messageForValidationReason("tooLarge"),
|
||||
413,
|
||||
{ details: { reason } },
|
||||
);
|
||||
}
|
||||
return errorJson(
|
||||
"validation_error",
|
||||
"File type or size is not allowed for this upload purpose.",
|
||||
messageForValidationReason(reason),
|
||||
400,
|
||||
{ details: { reason } },
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user