Skip to content

[Vulnerability] parse-community/parse-server: Stored XSS #566

Description

@github-actions

Potential Security Vulnerability Detected

Repository: parse-community/parse-server
Commit: be12a60
Author: Manuel
Date: 2026-06-16T00:42:38Z

Commit Message

fix: Stored XSS via non-standard file extension bypassing file upload extension blocklist ([GHSA-v8x7-r927-cc93](https://github.com/parse-community/parse-server/security/advisories/GHSA-v8x7-r927-cc93)) (#10505)

Pull Request

PR: #10505 - fix: Stored XSS via non-standard file extension bypassing file upload extension blocklist (GHSA-v8x7-r927-cc93)
Labels: state:released-alpha

Description:

Issue

Stored XSS via non-standard file extension bypassing file upload extension blocklist ([GHSA-v8x7-r927-cc93](GHSA-v8x7-r927-cc93))

Tasks

  • Add tests

Analysis

Vulnerability Type: Stored XSS
Severity: High

Description

The code allowed uploading files with dangerous content types combined with non-standard or appended file extensions (e.g., '.svg~', '.html.bak'), bypassing the upload extension blocklist. This enabled attackers to upload malicious files that could store and execute arbitrary scripts (Stored XSS) when served. The patch enhanced extension validation to also check content-type subtypes when file extensions are unrecognized or altered, preventing this bypass.

Affected Code

let extension = Utils.getFileExtension(filename);
// Strip MIME parameters (e.g. ";charset=utf-8") and whitespace
extension = extension?.split(';')[0]?.replace(/\s+/g, '');
// If the filename has no usable extension (no dot, trailing dot, or
// whitespace-only suffix), fall back to the Content-Type subtype — same
// as a dotless filename.
if (!extension && contentType && contentType.includes('/')) {
  extension = contentType.split('/')[1]?.split(';')[0]?.replace(/\s+/g, '');
}
// Last resort for malformed inputs (e.g. Content-Type without a slash):
// use the raw Content-Type so the existing rejection path still fires.
if (!extension && contentType) {
  extension = contentType.split(';')[0]?.replace(/\s+/g, '');
}

if (extension && !isValidExtension(extension)) {
  next(new Parse.Error(Parse.Error.FILE_SAVE_ERROR, `File upload of extension ${extension} is disabled.`));
  return;
}

Proof of Concept

Send a file upload POST request to the server with filename 'malicious.svg~' and Content-Type 'image/svg+xml' containing malicious SVG payload:

POST http://localhost:8378/1/files/malicious.svg~
Headers:
  Content-Type: application/json
Body:
{
  "_ApplicationId": "test",
  "_JavaScriptKey": "test",
  "_ContentType": "image/svg+xml",
  "base64": "PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnPjxzY3JpcHQ+YWxlcnQoMSk8L3NjcmlwdD48L3N2Zz4="
}

Before the patch, this upload would succeed bypassing the blocklist, storing a malicious SVG file with script execution capabilities that leads to Stored XSS. After the patch, the request is rejected with error 'File upload of extension svg+xml is disabled.'

This issue was automatically created by Vulnerability Spoiler Alert.
Detected at: 2026-06-16T06:01:03.989Z

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions