Skip to content

fix: GHSA-r899-h629-j84r#10523

Open
mtrezza wants to merge 1 commit into
parse-community:release-8.x.xfrom
mtrezza:fix/GHSA-r899-h629-j84r-v8
Open

fix: GHSA-r899-h629-j84r#10523
mtrezza wants to merge 1 commit into
parse-community:release-8.x.xfrom
mtrezza:fix/GHSA-r899-h629-j84r-v8

Conversation

@mtrezza

@mtrezza mtrezza commented Jun 24, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • Bug Fixes

    • Improved file upload validation for extension-based restrictions and content types.
    • Malformed file content types are now rejected with a clearer error when they can’t be validated.
    • Valid custom content types are accepted when allowed, and uploads with unrestricted extensions continue to work as expected.
  • Tests

    • Added coverage for invalid and valid file content type cases.
    • Updated purchase validation test data to use a standard text content type.

@parse-github-assistant

Copy link
Copy Markdown

🚀 Thanks for opening this pull request! We appreciate your effort in improving the project. Please let us know once your pull request is ready for review.

Tip

  • Keep pull requests small. Large PRs will be rejected. Break complex features into smaller, incremental PRs.
  • Use Test Driven Development. Write failing tests before implementing functionality. Ensure tests pass.
  • Group code into logical blocks. Add a short comment before each block to explain its purpose.
  • We offer conceptual guidance. Coding is up to you. PRs must be merge-ready for human review.
  • Our review focuses on concept, not quality. PRs with code issues will be rejected. Use an AI agent.
  • Human review time is precious. Avoid review ping-pong. Inspect and test your AI-generated code.

Note

Please respond to review comments from AI agents just like you would to comments from a human reviewer. Let the reviewer resolve their own comments, unless they have reviewed and accepted your commit, or agreed with your explanation for why the feedback was incorrect.

Caution

Pull requests must be written using an AI agent with human supervision. Pull requests written entirely by a human will likely be rejected, because of lower code quality, higher review effort and the higher risk of introducing bugs. Please note that AI review comments on this pull request alone do not satisfy this requirement. Our CI and AI review are safeguards, not development tools. If many issues are flagged, rethink your development approach. Invest more effort in planning and design rather than using review cycles to fix low-quality code.

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

FilesRouter.createHandler replaces its simple string-split Content-Type fallback with a structured type/subtype parser that rejects malformed values with "Invalid Content-Type.", skips validation when fileExtensions is ['*'], and validates the subtype against the blocklist. New tests cover these rejection and acceptance paths; a test fixture corrects 'text' to 'text/plain'.

Changes

FilesRouter Content-Type Validation Strictness

Layer / File(s) Summary
FilesRouter Content-Type parsing and rejection logic
src/Routers/FilesRouter.js
Replaces the old single-token string-split fallback with a conditional flow: skips when fileExtensions is ['*'], parses Content-Type as type/subtype, rejects malformed values with "Invalid Content-Type.", and validates the subtype against the extension blocklist.
New Content-Type validation tests and fixture fix
spec/ParseFile.spec.js, spec/PurchaseValidation.spec.js
Adds five fileExtensions test cases for malformed Content-Type rejection (FILE_SAVE_ERROR / "Invalid Content-Type."), wildcard-allow acceptance, and valid vendor subtype acceptance. Fixes createProduct() fixture from 'text' to 'text/plain'.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • parse-community/parse-server#10489: Modifies the same FilesRouter.createHandler upload validation path for filename extensions and parameterized Content-Type parsing.
  • parse-community/parse-server#10505: Modifies FilesRouter.createHandler's extension/Content-Type validation and extends spec/ParseFile.spec.js with fileExtensions-related rejection and acceptance cases.
  • parse-community/parse-server#10191: Directly modifies FilesRouter.js createHandler logic for parsing and validating Content-Type, including handling of MIME parameters and malformed type/subtype.
🚥 Pre-merge checks | ✅ 4 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning No PR description was provided, so the required Issue, Approach, and Tasks sections are missing. Add the repository's PR template sections with the linked issue, change summary, and completed task checklist.
Security Check ⚠️ Warning FilesRouter still accepts malformed non-empty Content-Type tokens (e.g. image//svg+xml, text/plain,application/json) because it only checks empty type/subtype and lacks token syntax validation. Validate type and subtype with a strict MIME token/parser before blocklist checks, and reject malformed values with FILE_SAVE_ERROR; add tests for non-empty malformed tokens.
Engage In Review Feedback ⚠️ Warning FilesRouter still only checks empty type/subtype and blocklist; the reviewed malformed cases (image//svg+xml, text/plain,application/json) aren't implemented or tested. Add syntax validation for media-type tokens and tests for the exact malformed cases, then get reviewer confirmation before resolving.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title uses the required fix: prefix and clearly matches the security fix in the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jun 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.47%. Comparing base (c9b24ce) to head (42acfe5).
⚠️ Report is 1 commits behind head on release-8.x.x.

Additional details and impacted files
@@                Coverage Diff                @@
##           release-8.x.x   #10523      +/-   ##
=================================================
+ Coverage          92.07%   92.47%   +0.39%     
=================================================
  Files                192      192              
  Lines              16175    16184       +9     
  Branches             234      234              
=================================================
+ Hits               14893    14966      +73     
+ Misses              1258     1198      -60     
+ Partials              24       20       -4     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@mtrezza

mtrezza commented Jun 24, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/Routers/FilesRouter.js`:
- Around line 227-256: The Content-Type parsing in FilesRouter’s save/validation
flow only rejects empty type/subtype pairs, so malformed tokens like double
slashes or comma-separated values can slip through. Tighten the validation
around the existing slashIndex/type/subtype parsing by rejecting any media-type
that is not a single well-formed token pair before reaching the blocklist check,
and keep using rejectExtension for blocked bare tokens while routing all other
malformed cases to the Invalid Content-Type Parse.Error path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b1713dc7-ebd3-4b07-9137-60d0c526e374

📥 Commits

Reviewing files that changed from the base of the PR and between 700a867 and 42acfe5.

📒 Files selected for processing (3)
  • spec/ParseFile.spec.js
  • spec/PurchaseValidation.spec.js
  • src/Routers/FilesRouter.js

Comment on lines +227 to +256
const slashIndex = contentType.indexOf('/');
const type = slashIndex > 0 ? contentType.slice(0, slashIndex).trim() : '';
const subtype =
slashIndex > 0 ? contentType.slice(slashIndex + 1).split(';')[0].trim() : '';
if (!type || !subtype) {
// A Content-Type that does not parse as `type/subtype` with a non-empty
// type AND subtype is malformed: there is no valid MIME type without a
// subtype (RFC 9110 §8.3.1). Browsers cannot parse it and fall back to
// MIME-sniffing the file body, which can render HTML/script markers as
// active content on storage adapters that serve the stored Content-Type
// (e.g. `image`, `image/`). Surface the precise blocklist message when
// the bare token names a blocked extension (e.g. a no-slash `svg`),
// otherwise reject the unparseable Content-Type.
const bareToken = (slashIndex < 0 ? contentType.split(';')[0] : type).replace(
/\s+/g,
''
);
if (bareToken && !isValidExtension(bareToken)) {
rejectExtension(bareToken);
return;
}
next(new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'Invalid Content-Type.'));
return;
}
// Validate the well-formed Content-Type subtype against the blocklist, e.g.
// "image/svg+xml" -> "svg+xml", "image/svg+xml;charset=utf-8" -> "svg+xml".
// Valid custom/vendor types (e.g. "application/vnd.api+json") parse and are
// allowed; only blocked subtypes are rejected.
const contentTypeExtension = subtype.replace(/\s+/g, '');
if (!isValidExtension(contentTypeExtension)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Reject invalid media-type tokens, not only empty type/subtype.

This path accepts non-empty but malformed values (for example image//svg+xml or text/plain,application/json) because it only checks emptiness before subtype blocklist validation. That weakens the malformed Content-Type hardening for unknown extensions.

Suggested fix
       if (!isExtensionRecognized && contentType && !allowsAllExtensions) {
         const slashIndex = contentType.indexOf('/');
         const type = slashIndex > 0 ? contentType.slice(0, slashIndex).trim() : '';
         const subtype =
           slashIndex > 0 ? contentType.slice(slashIndex + 1).split(';')[0].trim() : '';
+        const tokenRegex = /^[!#$%&'*+\-.^_`|~A-Za-z0-9]+$/;
+        const hasValidTypeSubtype = tokenRegex.test(type) && tokenRegex.test(subtype);
-        if (!type || !subtype) {
+        if (!type || !subtype || !hasValidTypeSubtype) {
           const bareToken = (slashIndex < 0 ? contentType.split(';')[0] : type).replace(
             /\s+/g,
             ''
           );
           if (bareToken && !isValidExtension(bareToken)) {
             rejectExtension(bareToken);
             return;
           }
           next(new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'Invalid Content-Type.'));
           return;
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const slashIndex = contentType.indexOf('/');
const type = slashIndex > 0 ? contentType.slice(0, slashIndex).trim() : '';
const subtype =
slashIndex > 0 ? contentType.slice(slashIndex + 1).split(';')[0].trim() : '';
if (!type || !subtype) {
// A Content-Type that does not parse as `type/subtype` with a non-empty
// type AND subtype is malformed: there is no valid MIME type without a
// subtype (RFC 9110 §8.3.1). Browsers cannot parse it and fall back to
// MIME-sniffing the file body, which can render HTML/script markers as
// active content on storage adapters that serve the stored Content-Type
// (e.g. `image`, `image/`). Surface the precise blocklist message when
// the bare token names a blocked extension (e.g. a no-slash `svg`),
// otherwise reject the unparseable Content-Type.
const bareToken = (slashIndex < 0 ? contentType.split(';')[0] : type).replace(
/\s+/g,
''
);
if (bareToken && !isValidExtension(bareToken)) {
rejectExtension(bareToken);
return;
}
next(new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'Invalid Content-Type.'));
return;
}
// Validate the well-formed Content-Type subtype against the blocklist, e.g.
// "image/svg+xml" -> "svg+xml", "image/svg+xml;charset=utf-8" -> "svg+xml".
// Valid custom/vendor types (e.g. "application/vnd.api+json") parse and are
// allowed; only blocked subtypes are rejected.
const contentTypeExtension = subtype.replace(/\s+/g, '');
if (!isValidExtension(contentTypeExtension)) {
const slashIndex = contentType.indexOf('/');
const type = slashIndex > 0 ? contentType.slice(0, slashIndex).trim() : '';
const subtype =
slashIndex > 0 ? contentType.slice(slashIndex + 1).split(';')[0].trim() : '';
const tokenRegex = /^[!#$%&'*+\-.^_`|~A-Za-z0-9]+$/;
const hasValidTypeSubtype = tokenRegex.test(type) && tokenRegex.test(subtype);
if (!type || !subtype || !hasValidTypeSubtype) {
// A Content-Type that does not parse as `type/subtype` with a non-empty
// type AND subtype is malformed: there is no valid MIME type without a
// subtype (RFC 9110 §8.3.1). Browsers cannot parse it and fall back to
// MIME-sniffing the file body, which can render HTML/script markers as
// active content on storage adapters that serve the stored Content-Type
// (e.g. `image`, `image/`). Surface the precise blocklist message when
// the bare token names a blocked extension (e.g. a no-slash `svg`),
// otherwise reject the unparseable Content-Type.
const bareToken = (slashIndex < 0 ? contentType.split(';')[0] : type).replace(
/\s+/g,
''
);
if (bareToken && !isValidExtension(bareToken)) {
rejectExtension(bareToken);
return;
}
next(new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'Invalid Content-Type.'));
return;
}
// Validate the well-formed Content-Type subtype against the blocklist, e.g.
// "image/svg+xml" -> "svg+xml", "image/svg+xml;charset=utf-8" -> "svg+xml".
// Valid custom/vendor types (e.g. "application/vnd.api+json") parse and are
// allowed; only blocked subtypes are rejected.
const contentTypeExtension = subtype.replace(/\s+/g, '');
if (!isValidExtension(contentTypeExtension)) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Routers/FilesRouter.js` around lines 227 - 256, The Content-Type parsing
in FilesRouter’s save/validation flow only rejects empty type/subtype pairs, so
malformed tokens like double slashes or comma-separated values can slip through.
Tighten the validation around the existing slashIndex/type/subtype parsing by
rejecting any media-type that is not a single well-formed token pair before
reaching the blocklist check, and keep using rejectExtension for blocked bare
tokens while routing all other malformed cases to the Invalid Content-Type
Parse.Error path.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant