mirror of
https://github.com/parse-community/parse-server
synced 2026-08-09 13:03:18 +00:00
fix: File upload Content-Type override via extension mismatch ([GHSA-vr5f-2r24-w5hc](https://github.com/parse-community/parse-server/security/advisories/GHSA-vr5f-2r24-w5hc)) (#10383)
This commit is contained in:
@@ -1663,6 +1663,88 @@ describe('Vulnerabilities', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('(GHSA-vr5f-2r24-w5hc) Stored XSS via Content-Type and file extension mismatch', () => {
|
||||
const headers = {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest',
|
||||
};
|
||||
|
||||
it('overrides mismatched Content-Type with extension-derived MIME type on buffered upload', async () => {
|
||||
const adapter = Config.get('test').filesController.adapter;
|
||||
const spy = spyOn(adapter, 'createFile').and.callThrough();
|
||||
const content = Buffer.from('<script>alert(1)</script>').toString('base64');
|
||||
await request({
|
||||
method: 'POST',
|
||||
url: 'http://localhost:8378/1/files/evil.txt',
|
||||
body: JSON.stringify({
|
||||
_ApplicationId: 'test',
|
||||
_JavaScriptKey: 'test',
|
||||
_ContentType: 'text/html',
|
||||
base64: content,
|
||||
}),
|
||||
headers,
|
||||
});
|
||||
expect(spy).toHaveBeenCalled();
|
||||
const contentTypeArg = spy.calls.mostRecent().args[2];
|
||||
expect(contentTypeArg).toBe('text/plain');
|
||||
});
|
||||
|
||||
it('overrides mismatched Content-Type with extension-derived MIME type on stream upload', async () => {
|
||||
const adapter = Config.get('test').filesController.adapter;
|
||||
const spy = spyOn(adapter, 'createFile').and.callThrough();
|
||||
const body = '<script>alert(1)</script>';
|
||||
await request({
|
||||
method: 'POST',
|
||||
url: 'http://localhost:8378/1/files/evil.txt',
|
||||
headers: {
|
||||
...headers,
|
||||
'Content-Type': 'text/html',
|
||||
'X-Parse-Upload-Mode': 'stream',
|
||||
},
|
||||
body,
|
||||
});
|
||||
expect(spy).toHaveBeenCalled();
|
||||
const contentTypeArg = spy.calls.mostRecent().args[2];
|
||||
expect(contentTypeArg).toBe('text/plain');
|
||||
});
|
||||
|
||||
it('preserves Content-Type when no file extension is present', async () => {
|
||||
const adapter = Config.get('test').filesController.adapter;
|
||||
const spy = spyOn(adapter, 'createFile').and.callThrough();
|
||||
await request({
|
||||
method: 'POST',
|
||||
url: 'http://localhost:8378/1/files/noextension',
|
||||
headers: {
|
||||
...headers,
|
||||
'Content-Type': 'image/png',
|
||||
},
|
||||
body: Buffer.from('fake png content'),
|
||||
});
|
||||
expect(spy).toHaveBeenCalled();
|
||||
const contentTypeArg = spy.calls.mostRecent().args[2];
|
||||
expect(contentTypeArg).toBe('image/png');
|
||||
});
|
||||
|
||||
it('infers Content-Type from extension when none is provided', async () => {
|
||||
const adapter = Config.get('test').filesController.adapter;
|
||||
const spy = spyOn(adapter, 'createFile').and.callThrough();
|
||||
const content = Buffer.from('test content').toString('base64');
|
||||
await request({
|
||||
method: 'POST',
|
||||
url: 'http://localhost:8378/1/files/data.txt',
|
||||
body: JSON.stringify({
|
||||
_ApplicationId: 'test',
|
||||
_JavaScriptKey: 'test',
|
||||
base64: content,
|
||||
}),
|
||||
headers,
|
||||
});
|
||||
expect(spy).toHaveBeenCalled();
|
||||
const contentTypeArg = spy.calls.mostRecent().args[2];
|
||||
expect(contentTypeArg).toBe('text/plain');
|
||||
});
|
||||
});
|
||||
|
||||
describe('(GHSA-q3vj-96h2-gwvg) SQL Injection via Increment amount on nested Object field', () => {
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
@@ -21,8 +21,8 @@ export class FilesController extends AdaptableController {
|
||||
const mime = (await import('mime')).default
|
||||
if (!hasExtension && contentType && mime.getExtension(contentType)) {
|
||||
filename = filename + '.' + mime.getExtension(contentType);
|
||||
} else if (hasExtension && !contentType) {
|
||||
contentType = mime.getType(filename);
|
||||
} else if (hasExtension) {
|
||||
contentType = mime.getType(filename) || contentType;
|
||||
}
|
||||
|
||||
if (!this.options.preserveFileName) {
|
||||
|
||||
Reference in New Issue
Block a user