Commit 29f61abc by Roy Committed by GitHub

fix: santinize the upload filename (#6159)

parent 4ea50bc3
...@@ -151,7 +151,7 @@ const getFormatedFilename = (filename?: string) => { ...@@ -151,7 +151,7 @@ const getFormatedFilename = (filename?: string) => {
// 先截断文件名,再进行格式化 // 先截断文件名,再进行格式化
const truncatedFilename = truncateFilename(filename); const truncatedFilename = truncateFilename(filename);
const extension = path.extname(truncatedFilename); // 带. const extension = path.extname(truncatedFilename); // 带.
const name = path.basename(truncatedFilename, extension); const name = sanitizeS3ObjectKey(path.basename(truncatedFilename, extension));
return { return {
formatedFilename: `${id}-${name}`, formatedFilename: `${id}-${name}`,
extension: extension.replace('.', '') extension: extension.replace('.', '')
...@@ -245,3 +245,14 @@ export function isS3ObjectKey<T extends keyof typeof S3Sources>( ...@@ -245,3 +245,14 @@ export function isS3ObjectKey<T extends keyof typeof S3Sources>(
): key is `${T}/${string}` { ): key is `${T}/${string}` {
return typeof key === 'string' && key.startsWith(`${S3Sources[source]}/`); 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'; ...@@ -31,7 +31,6 @@ import { postTextCensor } from '../../../../chat/postTextCensor';
import type { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io'; import type { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io';
import type { McpToolDataType } from '@fastgpt/global/core/app/tool/mcpTool/type'; import type { McpToolDataType } from '@fastgpt/global/core/app/tool/mcpTool/type';
import type { JSONSchemaInputType } from '@fastgpt/global/core/app/jsonschema'; import type { JSONSchemaInputType } from '@fastgpt/global/core/app/jsonschema';
import { getFileS3Key } from '../../../../../common/s3/utils';
type Response = DispatchNodeResultType<{ type Response = DispatchNodeResultType<{
[NodeOutputKeyEnum.answerText]: string; [NodeOutputKeyEnum.answerText]: string;
......
...@@ -18,8 +18,13 @@ async function handler( ...@@ -18,8 +18,13 @@ async function handler(
_: ApiResponseType<updateAvatarResponse> _: ApiResponseType<updateAvatarResponse>
): Promise<updateAvatarResponse> { ): Promise<updateAvatarResponse> {
const { filename, autoExpired } = req.body; const { filename, autoExpired } = req.body;
const { teamId } = await authCert({ req, authToken: true }); 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); export default NextAPI(handler);
...@@ -30,7 +30,12 @@ async function handler( ...@@ -30,7 +30,12 @@ async function handler(
}); });
await authUploadLimit(uid); 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); export default NextAPI(handler);
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', () => { ...@@ -265,6 +265,12 @@ describe('replaceS3KeyToPreviewUrl', () => {
expect(result).toContain('mock-jwt-token-dataset/team1/file{1}.png'); expect(result).toContain('mock-jwt-token-dataset/team1/file{1}.png');
}); });
it('文件名包含方括号应正常处理', () => {
const text = '![braces](dataset/team1/file[1].png)';
const result = replaceS3KeyToPreviewUrl(text, expiredTime);
expect(result).toContain('mock-jwt-token-dataset/team1/file[1].png');
});
// 引号 // 引号
it('alt 文本包含单引号应正常处理', () => { it('alt 文本包含单引号应正常处理', () => {
const text = "![it's a test](dataset/team1/file.png)"; const text = "![it's a test](dataset/team1/file.png)";
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment