mirror of
https://github.com/labring/FastGPT.git
synced 2026-01-29 01:15:28 +08:00
fix: santinize the upload filename (#6159)
This commit is contained in:
@@ -151,7 +151,7 @@ const getFormatedFilename = (filename?: string) => {
|
||||
// 先截断文件名,再进行格式化
|
||||
const truncatedFilename = truncateFilename(filename);
|
||||
const extension = path.extname(truncatedFilename); // 带.
|
||||
const name = path.basename(truncatedFilename, extension);
|
||||
const name = sanitizeS3ObjectKey(path.basename(truncatedFilename, extension));
|
||||
return {
|
||||
formatedFilename: `${id}-${name}`,
|
||||
extension: extension.replace('.', '')
|
||||
@@ -245,3 +245,14 @@ export function isS3ObjectKey<T extends keyof typeof S3Sources>(
|
||||
): key is `${T}/${string}` {
|
||||
return typeof key === 'string' && key.startsWith(`${S3Sources[source]}/`);
|
||||
}
|
||||
|
||||
export function sanitizeS3ObjectKey(key: string) {
|
||||
// 替换掉圆括号
|
||||
const replaceParentheses = (key: string) => {
|
||||
return key.replace(/[()]/g, (match) => (match === '(' ? '[' : ']'));
|
||||
};
|
||||
|
||||
key = replaceParentheses(key);
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ import { postTextCensor } from '../../../../chat/postTextCensor';
|
||||
import type { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io';
|
||||
import type { McpToolDataType } from '@fastgpt/global/core/app/tool/mcpTool/type';
|
||||
import type { JSONSchemaInputType } from '@fastgpt/global/core/app/jsonschema';
|
||||
import { getFileS3Key } from '../../../../../common/s3/utils';
|
||||
|
||||
type Response = DispatchNodeResultType<{
|
||||
[NodeOutputKeyEnum.answerText]: string;
|
||||
|
||||
@@ -18,8 +18,13 @@ async function handler(
|
||||
_: ApiResponseType<updateAvatarResponse>
|
||||
): Promise<updateAvatarResponse> {
|
||||
const { filename, autoExpired } = req.body;
|
||||
|
||||
const { teamId } = await authCert({ req, authToken: true });
|
||||
return await getS3AvatarSource().createUploadAvatarURL({ teamId, filename, autoExpired });
|
||||
return await getS3AvatarSource().createUploadAvatarURL({
|
||||
teamId,
|
||||
filename,
|
||||
autoExpired
|
||||
});
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
||||
@@ -30,7 +30,12 @@ async function handler(
|
||||
});
|
||||
await authUploadLimit(uid);
|
||||
|
||||
return await getS3ChatSource().createUploadChatFileURL({ appId, chatId, filename, uId: uid });
|
||||
return await getS3ChatSource().createUploadChatFileURL({
|
||||
appId,
|
||||
chatId,
|
||||
filename,
|
||||
uId: uid
|
||||
});
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
||||
47
test/cases/service/common/s3/utils.test.ts
Normal file
47
test/cases/service/common/s3/utils.test.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { sanitizeS3ObjectKey } from '@fastgpt/service/common/s3/utils';
|
||||
|
||||
describe('sanitizeS3ObjectKey', () => {
|
||||
it('should replace parentheses with square brackets', () => {
|
||||
expect(sanitizeS3ObjectKey('file(1).txt')).toBe('file[1].txt');
|
||||
expect(sanitizeS3ObjectKey('photo (copy).jpg')).toBe('photo [copy].jpg');
|
||||
expect(sanitizeS3ObjectKey('document(v2)(final).pdf')).toBe('document[v2][final].pdf');
|
||||
});
|
||||
|
||||
it('should replace opening parenthesis with opening bracket', () => {
|
||||
expect(sanitizeS3ObjectKey('test(')).toBe('test[');
|
||||
expect(sanitizeS3ObjectKey('((test')).toBe('[[test');
|
||||
});
|
||||
|
||||
it('should replace closing parenthesis with closing bracket', () => {
|
||||
expect(sanitizeS3ObjectKey('test)')).toBe('test]');
|
||||
expect(sanitizeS3ObjectKey('test))')).toBe('test]]');
|
||||
});
|
||||
|
||||
it('should handle multiple parentheses', () => {
|
||||
expect(sanitizeS3ObjectKey('a(b)c(d)e')).toBe('a[b]c[d]e');
|
||||
expect(sanitizeS3ObjectKey('((()))')).toBe('[[[]]]');
|
||||
});
|
||||
|
||||
it('should return unchanged string when no parentheses present', () => {
|
||||
expect(sanitizeS3ObjectKey('normal-file.txt')).toBe('normal-file.txt');
|
||||
expect(sanitizeS3ObjectKey('path/to/file.jpg')).toBe('path/to/file.jpg');
|
||||
expect(sanitizeS3ObjectKey('file_name_123.pdf')).toBe('file_name_123.pdf');
|
||||
});
|
||||
|
||||
it('should handle empty string', () => {
|
||||
expect(sanitizeS3ObjectKey('')).toBe('');
|
||||
});
|
||||
|
||||
it('should preserve existing square brackets', () => {
|
||||
expect(sanitizeS3ObjectKey('file[1].txt')).toBe('file[1].txt');
|
||||
expect(sanitizeS3ObjectKey('file[1](2).txt')).toBe('file[1][2].txt');
|
||||
});
|
||||
|
||||
it('should handle S3 key paths with parentheses', () => {
|
||||
expect(sanitizeS3ObjectKey('dataset/uploads/file (1).pdf')).toBe(
|
||||
'dataset/uploads/file [1].pdf'
|
||||
);
|
||||
expect(sanitizeS3ObjectKey('chat/images/photo(copy).jpg')).toBe('chat/images/photo[copy].jpg');
|
||||
});
|
||||
});
|
||||
@@ -265,6 +265,12 @@ describe('replaceS3KeyToPreviewUrl', () => {
|
||||
expect(result).toContain('mock-jwt-token-dataset/team1/file{1}.png');
|
||||
});
|
||||
|
||||
it('文件名包含方括号应正常处理', () => {
|
||||
const text = '';
|
||||
const result = replaceS3KeyToPreviewUrl(text, expiredTime);
|
||||
expect(result).toContain('mock-jwt-token-dataset/team1/file[1].png');
|
||||
});
|
||||
|
||||
// 引号
|
||||
it('alt 文本包含单引号应正常处理', () => {
|
||||
const text = "";
|
||||
|
||||
Reference in New Issue
Block a user