mirror of
https://github.com/parse-community/parse-server
synced 2026-08-09 13:03:18 +00:00
fix: File creation and deletion bypasses readOnlyMasterKey write restriction (GHSA-xfh7-phr7-gr2x) (#10096)
This commit is contained in:
@@ -1307,6 +1307,63 @@ describe('read-only masterKey', () => {
|
||||
});
|
||||
expect(Array.isArray(res.data)).toBe(true);
|
||||
});
|
||||
|
||||
it('should throw when trying to delete a file with readOnlyMasterKey', async () => {
|
||||
// Create a file with the real master key
|
||||
const uploadRes = await request({
|
||||
method: 'POST',
|
||||
url: `${Parse.serverURL}/files/readonly-delete-test.txt`,
|
||||
headers: {
|
||||
'X-Parse-Application-Id': Parse.applicationId,
|
||||
'X-Parse-Master-Key': Parse.masterKey,
|
||||
'Content-Type': 'text/plain',
|
||||
},
|
||||
body: 'file content',
|
||||
});
|
||||
const filename = uploadRes.data.name;
|
||||
expect(filename).toBeDefined();
|
||||
|
||||
// Attempt delete with readOnlyMasterKey — should be rejected
|
||||
loggerErrorSpy.calls.reset();
|
||||
try {
|
||||
await request({
|
||||
method: 'DELETE',
|
||||
url: `${Parse.serverURL}/files/${filename}`,
|
||||
headers: {
|
||||
'X-Parse-Application-Id': Parse.applicationId,
|
||||
'X-Parse-Master-Key': 'read-only-test',
|
||||
},
|
||||
});
|
||||
fail('should have thrown');
|
||||
} catch (res) {
|
||||
expect(res.status).toBe(403);
|
||||
expect(res.data.error).toBe('Permission denied');
|
||||
}
|
||||
|
||||
// Verify file still exists
|
||||
const getRes = await request({ url: uploadRes.data.url });
|
||||
expect(getRes.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should throw when trying to create a file with readOnlyMasterKey', async () => {
|
||||
loggerErrorSpy.calls.reset();
|
||||
try {
|
||||
await request({
|
||||
method: 'POST',
|
||||
url: `${Parse.serverURL}/files/readonly-create-test.txt`,
|
||||
headers: {
|
||||
'X-Parse-Application-Id': Parse.applicationId,
|
||||
'X-Parse-Master-Key': 'read-only-test',
|
||||
'Content-Type': 'text/plain',
|
||||
},
|
||||
body: 'file content',
|
||||
});
|
||||
fail('should have thrown');
|
||||
} catch (res) {
|
||||
expect(res.status).toBe(403);
|
||||
expect(res.data.error).toBe('Permission denied');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('rest context', () => {
|
||||
|
||||
@@ -5,6 +5,7 @@ import Config from '../Config';
|
||||
import logger from '../logger';
|
||||
const triggers = require('../triggers');
|
||||
const Utils = require('../Utils');
|
||||
import { createSanitizedHttpError } from '../Error';
|
||||
|
||||
export class FilesRouter {
|
||||
expressRouter({ maxUploadSize = '20Mb' } = {}) {
|
||||
@@ -112,6 +113,12 @@ export class FilesRouter {
|
||||
}
|
||||
|
||||
async createHandler(req, res, next) {
|
||||
if (req.auth.isReadOnly) {
|
||||
const error = createSanitizedHttpError(403, "read-only masterKey isn't allowed to create a file.", req.config);
|
||||
res.status(error.status);
|
||||
res.end(`{"error":"${error.message}"}`);
|
||||
return;
|
||||
}
|
||||
const config = req.config;
|
||||
const user = req.auth.user;
|
||||
const isMaster = req.auth.isMaster;
|
||||
@@ -266,6 +273,12 @@ export class FilesRouter {
|
||||
}
|
||||
|
||||
async deleteHandler(req, res, next) {
|
||||
if (req.auth.isReadOnly) {
|
||||
const error = createSanitizedHttpError(403, "read-only masterKey isn't allowed to delete a file.", req.config);
|
||||
res.status(error.status);
|
||||
res.end(`{"error":"${error.message}"}`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const { filesController } = req.config;
|
||||
const { filename } = req.params;
|
||||
|
||||
Reference in New Issue
Block a user