Skip to content

Commit f203034

Browse files
Fix potential IDOR vulnerability in workspace parsed files endpoints
Add ownership validation to prevent users from deleting or embedding parsed files that don't belong to them. Previously, the delete and embed endpoints only validated authentication but not resource ownership, allowing users to delete attached files for users within workspaces they are also a member of. Changes: - Delete endpoint now filters by userId and workspaceId - Embed endpoint validates file belongs to user and workspace (redundant) - delete() returns false when no matching records found (returns 403) - Added JSDoc comments for clarity GHSA-p5rf-8p88-979c
1 parent 6b2ed8e commit f203034

File tree

2 files changed

+33
-7
lines changed

2 files changed

+33
-7
lines changed

server/endpoints/workspacesParsedFiles.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,16 @@ function workspaceParsedFilesEndpoints(app) {
5050
try {
5151
const { fileIds = [] } = reqBody(request);
5252
if (!fileIds.length) return response.sendStatus(400).end();
53+
const user = await userFromSession(request, response);
54+
const workspace = response.locals.workspace;
5355
const success = await WorkspaceParsedFiles.delete({
54-
id: { in: fileIds.map((id) => parseInt(id)) },
56+
id: {
57+
in: fileIds.map((id) => parseInt(id)),
58+
},
59+
...(user ? { userId: user.id } : {}),
60+
workspaceId: workspace.id,
5561
});
56-
return response.status(success ? 200 : 500).end();
62+
return response.status(success ? 200 : 403).end();
5763
} catch (e) {
5864
console.error(e.message, e);
5965
return response.sendStatus(500).end();
@@ -77,7 +83,11 @@ function workspaceParsedFilesEndpoints(app) {
7783

7884
if (!fileId) return response.sendStatus(400).end();
7985
const { success, error, document } =
80-
await WorkspaceParsedFiles.moveToDocumentsAndEmbed(fileId, workspace);
86+
await WorkspaceParsedFiles.moveToDocumentsAndEmbed(
87+
user,
88+
fileId,
89+
workspace
90+
);
8191

8292
if (!success) {
8393
return response.status(500).json({

server/models/workspaceParsedFiles.js

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ const WorkspaceParsedFiles = {
4343
}
4444
},
4545

46+
/**
47+
* Gets a parsed file by its ID or a clause.
48+
* @param {object} clause - The clause to filter the parsed files.
49+
* @returns {Promise<import("@prisma/client").workspace_parsed_files | null>} The parsed file.
50+
*/
4651
get: async function (clause = {}) {
4752
try {
4853
const file = await prisma.workspace_parsed_files.findFirst({
@@ -77,10 +82,10 @@ const WorkspaceParsedFiles = {
7782

7883
delete: async function (clause = {}) {
7984
try {
80-
await prisma.workspace_parsed_files.deleteMany({
85+
const result = await prisma.workspace_parsed_files.deleteMany({
8186
where: clause,
8287
});
83-
return true;
88+
return result.count > 0;
8489
} catch (error) {
8590
console.error(error.message);
8691
return false;
@@ -95,9 +100,20 @@ const WorkspaceParsedFiles = {
95100
return _sum.tokenCountEstimate || 0;
96101
},
97102

98-
moveToDocumentsAndEmbed: async function (fileId, workspace) {
103+
/**
104+
* Moves a parsed file to the documents and embeds it.
105+
* @param {import("@prisma/client").users | null} user - The user performing the operation.
106+
* @param {number} fileId - The ID of the parsed file.
107+
* @param {import("@prisma/client").workspaces} workspace - The workspace the file belongs to.
108+
* @returns {Promise<{ success: boolean, error: string | null, document: import("@prisma/client").workspace_documents | null }>} The result of the operation.
109+
*/
110+
moveToDocumentsAndEmbed: async function (user = null, fileId, workspace) {
99111
try {
100-
const parsedFile = await this.get({ id: parseInt(fileId) });
112+
const parsedFile = await this.get({
113+
id: parseInt(fileId),
114+
...(user ? { userId: user.id } : {}),
115+
workspaceId: workspace.id,
116+
});
101117
if (!parsedFile) throw new Error("File not found");
102118

103119
// Get file location from metadata

0 commit comments

Comments
 (0)