mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-03 17:53:14 +00:00
fix(vendor-portal): validate uploads against the server allowlist
Any image/* or video/* type passed the client check, so GIF, WebP and WebM were only rejected after upload. Resolve the kind from the same allowlist the server uses: an allowlisted browser type, otherwise the extension.
This commit is contained in:
parent
44e96cf2a3
commit
f65a48077d
2 changed files with 51 additions and 8 deletions
|
|
@ -14,15 +14,32 @@ const UNSUPPORTED_TYPE_MESSAGE = "Only PDF, JPG, PNG, HEIC, MP4, and MOV files a
|
|||
|
||||
type VendorUploadKind = "document" | "photo" | "video";
|
||||
|
||||
// Mirrors the server allowlist: an allowlisted browser type wins, otherwise the
|
||||
// extension decides (mobile browsers often send an empty or octet-stream type).
|
||||
const KIND_BY_MIME: Record<string, VendorUploadKind> = {
|
||||
"application/pdf": "document",
|
||||
"image/jpeg": "photo",
|
||||
"image/jpg": "photo",
|
||||
"image/png": "photo",
|
||||
"image/heic": "photo",
|
||||
"video/mp4": "video",
|
||||
"video/quicktime": "video",
|
||||
};
|
||||
|
||||
const KIND_BY_EXTENSION: Record<string, VendorUploadKind> = {
|
||||
pdf: "document",
|
||||
jpg: "photo",
|
||||
jpeg: "photo",
|
||||
png: "photo",
|
||||
heic: "photo",
|
||||
mp4: "video",
|
||||
mov: "video",
|
||||
};
|
||||
|
||||
function resolveKind(file: File): VendorUploadKind | null {
|
||||
const extension = file.name.toLowerCase().split(".").pop() ?? "";
|
||||
const mime = file.type.toLowerCase();
|
||||
if (mime.startsWith("video/") || extension === "mp4" || extension === "mov") return "video";
|
||||
if (mime.startsWith("image/") || ["jpg", "jpeg", "png", "heic"].includes(extension)) {
|
||||
return "photo";
|
||||
}
|
||||
if (mime === "application/pdf" || extension === "pdf") return "document";
|
||||
return null;
|
||||
const name = file.name.toLowerCase();
|
||||
const extension = name.includes(".") ? name.slice(name.lastIndexOf(".") + 1) : "";
|
||||
return KIND_BY_MIME[file.type.toLowerCase()] ?? KIND_BY_EXTENSION[extension] ?? null;
|
||||
}
|
||||
|
||||
export async function validateVendorDocument(file: File): Promise<string | undefined> {
|
||||
|
|
|
|||
|
|
@ -65,6 +65,32 @@ describe("VendorPortalDocuments", () => {
|
|||
expect(screen.getByText("Processing")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it.each([
|
||||
["animation.gif", "image/gif"],
|
||||
["clip.webm", "video/webm"],
|
||||
])("rejects %s (%s), which the server does not accept", async (name, type) => {
|
||||
const upload = vi.spyOn(vendorPortalApi, "uploadDocument");
|
||||
renderWithProviders(
|
||||
<VendorPortalDocuments
|
||||
token="portal-token"
|
||||
dispatchId={7}
|
||||
documents={[]}
|
||||
locked={false}
|
||||
onChanged={vi.fn()}
|
||||
/>,
|
||||
{ withAuth: false },
|
||||
);
|
||||
|
||||
fireEvent.change(screen.getByLabelText("Choose completion document"), {
|
||||
target: { files: [new File(["x"], name, { type })] },
|
||||
});
|
||||
|
||||
expect(await screen.findByRole("alert")).toHaveTextContent(
|
||||
"Only PDF, JPG, PNG, HEIC, MP4, and MOV files are allowed.",
|
||||
);
|
||||
expect(upload).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("SH-116 media contract", () => {
|
||||
const MB = 1_000_000;
|
||||
const renderDocuments = () =>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue