修复采购附件同源 XSS(07-17-purchase-attachment-xss-hardening)
这个提交包含在:
@@ -333,6 +333,52 @@ export function mapApiErrorMessage(
|
||||
return "请假冻结账本状态异常,请联系管理员处理。";
|
||||
}
|
||||
|
||||
if (details?.attachment === "security_validation_failed") {
|
||||
if (details?.reason === "quarantined") {
|
||||
return "附件已隔离,无法下载,请联系管理员。";
|
||||
}
|
||||
if (details?.reason === "missing_object") {
|
||||
return "附件文件不存在或已缺失,请联系管理员。";
|
||||
}
|
||||
return "附件未通过安全校验,请重新上传允许格式或联系管理员。";
|
||||
}
|
||||
|
||||
if (details?.file === "empty_file") {
|
||||
return "附件内容为空,请重新选择文件。";
|
||||
}
|
||||
|
||||
if (details?.file === "size_limit" || details?.file === "file_too_large") {
|
||||
return "附件超过大小限制,请压缩后重新上传。";
|
||||
}
|
||||
|
||||
if (details?.file === "dangerous_filename") {
|
||||
return "附件名称不安全,请重命名后重新上传。";
|
||||
}
|
||||
|
||||
if (details?.file === "unsupported_type") {
|
||||
return "仅支持 PDF、JPG、PNG、WEBP 文件,请重新选择。";
|
||||
}
|
||||
|
||||
if (details?.file === "type_mismatch") {
|
||||
return "附件扩展名、声明类型与实际内容不一致,请重新选择原始文件或转换为支持格式。";
|
||||
}
|
||||
|
||||
if (details?.file === "corrupt_content") {
|
||||
return "附件已损坏或内容不完整,请重新导出后上传。";
|
||||
}
|
||||
|
||||
if (details?.file === "pixel_limit") {
|
||||
return "图片尺寸过大,请压缩后重新上传。";
|
||||
}
|
||||
|
||||
if (details?.file === "multiple_pages") {
|
||||
return "暂不支持多页或动画图片,请转换为 PDF 或单页安全图片。";
|
||||
}
|
||||
|
||||
if (details?.file === "only_single_file_supported") {
|
||||
return "一次只能上传一个附件,请重新选择。";
|
||||
}
|
||||
|
||||
if (details?.file === "required") {
|
||||
return "当前假种要求上传附件后才能提交。";
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { apiRequestData } from "./http";
|
||||
import { apiRequestBlob, apiRequestData } from "./http";
|
||||
import type { AttachmentGovernanceFields } from "../lib/attachmentGovernance";
|
||||
import type { PrecheckResponse, RiskFlag } from "./feeControl";
|
||||
|
||||
@@ -386,11 +386,12 @@ export async function deletePurchaseAttachment(purchaseRequestId: string, attach
|
||||
);
|
||||
}
|
||||
|
||||
export async function issuePurchaseAttachmentSignedUrl(purchaseRequestId: string, attachmentId: string, reason?: string) {
|
||||
return apiRequestData<{ signedUrl: string; expiresAt: string }>(
|
||||
`/api/purchase-requests/${purchaseRequestId}/attachments/${attachmentId}/signed-url`,
|
||||
export async function downloadPurchaseAttachment(purchaseRequestId: string, attachmentId: string, reason?: string) {
|
||||
return apiRequestBlob(
|
||||
`/api/purchase-requests/${purchaseRequestId}/attachments/${attachmentId}/download`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify(reason ? { reason } : {}),
|
||||
},
|
||||
);
|
||||
|
||||
在新工单中引用
屏蔽一个用户