fix: 兼容客户端文档 MIME 差异
- 接受 WPS及系统 MIME 别名并统一保存服务端标准类型 - 保留扩展名、大小和文件头校验以拒绝伪装文件
This commit is contained in:
@@ -56,7 +56,7 @@ public class AgentDocumentService {
|
||||
private static final byte[] OLE_SIGNATURE = {
|
||||
(byte) 0xd0, (byte) 0xcf, 0x11, (byte) 0xe0, (byte) 0xa1, (byte) 0xb1, 0x1a, (byte) 0xe1
|
||||
};
|
||||
private static final Map<String, Set<String>> MIME_TYPES = mimeTypes();
|
||||
private static final Map<String, String> CANONICAL_MIME_TYPES = canonicalMimeTypes();
|
||||
|
||||
private final AgentDocumentAttachmentMapper attachmentMapper;
|
||||
private final AgentDocumentSnapshotMapper snapshotMapper;
|
||||
@@ -701,8 +701,8 @@ public class AgentDocumentService {
|
||||
throw new ResponseStatusException(HttpStatus.PAYLOAD_TOO_LARGE,
|
||||
"该类型文档不能超过 " + limit / (1024 * 1024) + " MiB");
|
||||
}
|
||||
String mimeType = normalizeMime(file.getContentType(), extension);
|
||||
validateSignature(file, extension);
|
||||
String mimeType = normalizeMime(file.getContentType(), extension);
|
||||
return new DocumentFile(originalName, extension, mimeType, file.getSize());
|
||||
}
|
||||
|
||||
@@ -768,15 +768,27 @@ public class AgentDocumentService {
|
||||
return properties.getLimits().getOfficeMaxBytes().toBytes();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将客户端声明的 MIME 规范为服务端可信类型。
|
||||
*
|
||||
* @param declared 客户端声明 MIME
|
||||
* @param extension 已校验扩展名
|
||||
* @return 服务端规范 MIME
|
||||
*/
|
||||
private String normalizeMime(String declared, String extension) {
|
||||
String normalized = declared == null ? "" : declared.split(";", 2)[0].trim().toLowerCase(Locale.ROOT);
|
||||
Set<String> allowed = MIME_TYPES.get(extension);
|
||||
if (StringUtils.hasText(normalized) && !"application/octet-stream".equals(normalized)
|
||||
&& (allowed == null || !allowed.contains(normalized))) {
|
||||
throw new ResponseStatusException(HttpStatus.UNSUPPORTED_MEDIA_TYPE,
|
||||
"文件 MIME 类型与扩展名不一致");
|
||||
String canonical = CANONICAL_MIME_TYPES.get(extension);
|
||||
if (!StringUtils.hasText(canonical)) {
|
||||
throw new ResponseStatusException(HttpStatus.UNSUPPORTED_MEDIA_TYPE, "不支持该文档类型");
|
||||
}
|
||||
return allowed.iterator().next();
|
||||
// 浏览器 MIME 由系统注册表或桌面 MIME 数据库决定,只用于诊断,最终类型由扩展名和文件内容校验确定。
|
||||
if (StringUtils.hasText(normalized)
|
||||
&& !"application/octet-stream".equals(normalized)
|
||||
&& !canonical.equals(normalized)) {
|
||||
LOG.info("Normalize Agent document MIME alias: extension={}, declaredMime={}, canonicalMime={}",
|
||||
extension, normalized, canonical);
|
||||
}
|
||||
return canonical;
|
||||
}
|
||||
|
||||
private String safeOriginalName(String value) {
|
||||
@@ -904,17 +916,22 @@ public class AgentDocumentService {
|
||||
return new ResponseStatusException(HttpStatus.BAD_REQUEST, message);
|
||||
}
|
||||
|
||||
private static Map<String, Set<String>> mimeTypes() {
|
||||
Map<String, Set<String>> values = new LinkedHashMap<>();
|
||||
values.put("pdf", Set.of("application/pdf"));
|
||||
values.put("doc", Set.of("application/msword"));
|
||||
values.put("docx", Set.of("application/vnd.openxmlformats-officedocument.wordprocessingml.document"));
|
||||
values.put("ppt", Set.of("application/vnd.ms-powerpoint"));
|
||||
values.put("pptx", Set.of("application/vnd.openxmlformats-officedocument.presentationml.presentation"));
|
||||
values.put("xls", Set.of("application/vnd.ms-excel"));
|
||||
values.put("xlsx", Set.of("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"));
|
||||
values.put("txt", Set.of("text/plain"));
|
||||
values.put("md", Set.of("text/markdown", "text/plain"));
|
||||
/**
|
||||
* 创建受支持扩展名到服务端规范 MIME 的映射。
|
||||
*
|
||||
* @return 不可变规范 MIME 映射
|
||||
*/
|
||||
private static Map<String, String> canonicalMimeTypes() {
|
||||
Map<String, String> values = new LinkedHashMap<>();
|
||||
values.put("pdf", "application/pdf");
|
||||
values.put("doc", "application/msword");
|
||||
values.put("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
|
||||
values.put("ppt", "application/vnd.ms-powerpoint");
|
||||
values.put("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation");
|
||||
values.put("xls", "application/vnd.ms-excel");
|
||||
values.put("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
values.put("txt", "text/plain");
|
||||
values.put("md", "text/markdown");
|
||||
return Map.copyOf(values);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user