mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-04 14:57:42 +00:00
🌐 fix: Percent-encode X-File-Metadata header for Unicode filenames (#12983)
* 🌐 fix: Percent-encode X-File-Metadata header for Unicode filenames After #12977 preserved Unicode in filenames, the download route crashes with ERR_INVALID_CHAR because JSON.stringify(file) now contains non-ASCII characters that Node.js rejects in HTTP headers per RFC 7230. Wrap the header value in encodeURIComponent on the server and decodeURIComponent on the client before JSON.parse. * fix: Update file route tests after dev merge --------- Co-authored-by: Danny Avila <danny@librechat.ai>
This commit is contained in:
parent
5c338a4642
commit
5efbcb8b93
3 changed files with 18 additions and 11 deletions
|
|
@ -220,14 +220,16 @@ router.delete('/', async (req, res) => {
|
|||
const agentFiles = files
|
||||
.filter((f) => toolResourceFiles.includes(f.file_id))
|
||||
.map((file) => ({ tool_resource: req.body.tool_resource, file_id: file.file_id }));
|
||||
const accessMap = await hasAccessToFilesViaAgent({
|
||||
userId: req.user.id,
|
||||
role: req.user.role,
|
||||
fileIds: agentFiles.map((file) => file.file_id),
|
||||
agentId: req.body.agent_id,
|
||||
isDelete: true,
|
||||
});
|
||||
const unauthorizedFiles = agentFiles.filter((file) => !accessMap.get(file.file_id));
|
||||
const hasAgentEditAccess =
|
||||
agent.author?.toString() === req.user.id.toString() ||
|
||||
(await checkPermission({
|
||||
userId: req.user.id,
|
||||
role: req.user.role,
|
||||
resourceType: ResourceType.AGENT,
|
||||
resourceId: agent._id,
|
||||
requiredPermission: PermissionBits.EDIT,
|
||||
}));
|
||||
const unauthorizedFiles = hasAgentEditAccess ? [] : agentFiles;
|
||||
if (unauthorizedFiles.length > 0) {
|
||||
return res.status(403).json({
|
||||
message: 'You can only delete files you have access to',
|
||||
|
|
@ -519,7 +521,10 @@ router.get('/download/:userId/:file_id', fileAccess, async (req, res) => {
|
|||
const setHeaders = () => {
|
||||
res.setHeader('Content-Disposition', getContentDisposition(file.filename));
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.setHeader('X-File-Metadata', JSON.stringify(getDownloadFileMetadata(file)));
|
||||
res.setHeader(
|
||||
'X-File-Metadata',
|
||||
encodeURIComponent(JSON.stringify(getDownloadFileMetadata(file))),
|
||||
);
|
||||
};
|
||||
|
||||
if (checkOpenAIStorage(file.source)) {
|
||||
|
|
|
|||
|
|
@ -685,7 +685,7 @@ describe('File Routes - Delete with Agent Access', () => {
|
|||
expect(response.status).toBe(200);
|
||||
expect(response.body.toString()).toBe('file content');
|
||||
expect(response.headers.location).toBeUndefined();
|
||||
const metadata = JSON.parse(response.headers['x-file-metadata']);
|
||||
const metadata = JSON.parse(decodeURIComponent(response.headers['x-file-metadata']));
|
||||
expect(metadata).toMatchObject({
|
||||
file_id: userFileId,
|
||||
filename: 'file.pdf',
|
||||
|
|
|
|||
|
|
@ -96,7 +96,9 @@ export const useFileDownload = (
|
|||
const blob = response.data;
|
||||
const downloadURL = window.URL.createObjectURL(blob);
|
||||
try {
|
||||
const metadata: t.TFile | undefined = JSON.parse(response.headers['x-file-metadata']);
|
||||
const metadata: t.TFile | undefined = JSON.parse(
|
||||
decodeURIComponent(response.headers['x-file-metadata']),
|
||||
);
|
||||
if (!metadata) {
|
||||
console.warn('No metadata found for file download', response.headers);
|
||||
return downloadURL;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue