Skip to content

fix: Preserve UTF-8 GraphQL upload filenames with latest graphql-upload#10478

Open
coratgerl wants to merge 11 commits into
parse-community:alphafrom
coratgerl:fix-file-encodage
Open

fix: Preserve UTF-8 GraphQL upload filenames with latest graphql-upload#10478
coratgerl wants to merge 11 commits into
parse-community:alphafrom
coratgerl:fix-file-encodage

Conversation

@coratgerl

@coratgerl coratgerl commented May 26, 2026

Copy link
Copy Markdown
Contributor

Summary

  • upgrade graphql-upload to the latest ESM-only release while keeping Parse Server on its current public module model via a small private compatibility helper
  • preserve accented GraphQL upload filenames end-to-end by forcing UTF-8 multipart filename decoding, encoding the internal /files handoff safely, and allowing accented characters through filename validation
  • add a GraphQL regression test covering café.txt uploads and keep existing GraphQL upload coverage green

This PR was done in TDD.

Test plan

  • Run the focused GraphQL regression for accented filenames
  • Run spec/ParseGraphQLServer.spec.js
  • Run targeted spec/ParseFile.spec.js filename checks
  • Run npm run lint
  • Run npm test

Notes

  • npm test finishes with one unrelated pre-existing failure in Parse.Query testing order by _updated_at

Made with Cursor

Summary by CodeRabbit

  • New Features
    • Unicode filename normalization (NFC) and filepath validation across upload, storage, routing, and retrieval, including blocked reserved segments.
    • GraphQL multipart uploads via a lazy-loaded middleware; schema loading is now memoized so concurrent callers share the same initialization.
  • Bug Fixes
    • Improved upload proxy error handling: always JSON-parse responses and reject non-2xx requests consistently; GraphQL upload request paths now URL-encode non-ASCII filenames.
  • Tests
    • Added/expanded REST and GraphQL upload tests (including accented filenames) plus concurrency and validation regression coverage.
  • Chores
    • Upgraded graphql-upload to 17.0.0.

Load the latest ESM-only graphql-upload through a small compatibility layer so GraphQL uploads keep accented filenames intact without changing Parse Server's public module model.

Co-authored-by: Cursor <cursoragent@cursor.com>
@parse-github-assistant

Copy link
Copy Markdown

I will reformat the title to use the proper commit message syntax.

@parse-github-assistant parse-github-assistant Bot changed the title fix: preserve UTF-8 GraphQL upload filenames with latest graphql-upload fix: Preserve UTF-8 GraphQL upload filenames with latest graphql-upload May 26, 2026
@parse-github-assistant

parse-github-assistant Bot commented May 26, 2026

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 May 26, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2fbfa22f-3867-4920-9f1b-3fb6ff904a28

📥 Commits

Reviewing files that changed from the base of the PR and between 8856dec and 026ae5d.

📒 Files selected for processing (4)
  • package-lock.json
  • package.json
  • spec/ParseFile.spec.js
  • src/Routers/FilesRouter.js
🚧 Files skipped from review as they are similar to previous changes (4)
  • spec/ParseFile.spec.js
  • package-lock.json
  • package.json
  • src/Routers/FilesRouter.js

📝 Walkthrough

Walkthrough

graphql-upload is upgraded to v17, GraphQL upload handling uses cached dynamic imports, schema loading defers upload type initialization, file paths are normalized and validated across file operations, and tests add accented-filename and path-validation coverage.

Changes

GraphQL Upload ESM Migration and Unicode Support

Layer / File(s) Summary
Dependency upgrade to graphql-upload v17
package.json, package-lock.json
graphql-upload is bumped to 17.0.0, and the lockfile updates the related fs-capacitor and graphql-upload entries, including resolved, integrity, engines, and peer dependency metadata.
GraphQL upload dynamic import adapter
src/GraphQL/helpers/graphqlUpload.js, src/GraphQL/ParseGraphQLServer.js
A cached dynamic-import helper is added for graphql-upload ESM modules, along with multipart middleware that only runs for multipart/form-data; ParseGraphQLServer mounts that middleware instead of graphqlUploadExpress.
Deferred GraphQL upload type loading
src/GraphQL/loaders/defaultGraphQLTypes.js, src/GraphQL/ParseGraphQLSchema.js
defaultGraphQLTypes.load() now awaits getGraphQLUpload() before building GraphQLUpload and FILE_INPUT, and ParseGraphQLSchema.load() memoizes in-flight loads while awaiting the default types first.
Filename normalization and path validation
src/Adapters/Files/FilesAdapter.js
FilesAdapter adds NFC normalization, Unicode-aware filename validation, and filepath validation for reserved segments, traversal, and per-segment checks.
Files controller, router, and GridFS normalization
src/Controllers/FilesController.js, src/Routers/FilesRouter.js, src/Adapters/Files/GridFSBucketAdapter.js
FilesController, FilesRouter, and GridFSBucketAdapter normalize filenames before file operations, validate filepath inputs at the router boundary, and source reserved segments from the shared adapter constant.
GraphQL file mutation proxy behavior
src/GraphQL/loaders/filesMutations.js
Proxied upload paths now URL-encode each path segment, and proxy responses are always parsed as JSON before success or error handling.
Accented filename and path validation tests
spec/ParseGraphQLSchema.spec.js, spec/graphqlUpload.spec.js, spec/ParseGraphQLServer.spec.js, spec/ParseFile.spec.js, spec/FileNameNormalization.spec.js, spec/FilesController.spec.js
The specs add coverage for cached schema loading, multipart upload middleware and proxy failures, accented filename uploads through GraphQL and REST, GridFS normalization, controller validation, and traversal or reserved filepath rejection.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ParseGraphQLServer
  participant createGraphQLUploadMiddleware
  participant getGraphQLUpload
  participant defaultGraphQLTypes
  participant FilesController
  participant GridFSBucketAdapter
  Client->>ParseGraphQLServer: multipart upload request
  ParseGraphQLServer->>createGraphQLUploadMiddleware: mount upload middleware
  createGraphQLUploadMiddleware->>getGraphQLUpload: load GraphQLUpload
  ParseGraphQLServer->>defaultGraphQLTypes: load schema types
  ParseGraphQLServer->>FilesController: normalizeFilename / validateFilepath
  FilesController->>GridFSBucketAdapter: normalized file operation
  GridFSBucketAdapter-->>Client: stored file response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • mtrezza
  • Moumouls

Possibly related PRs

🚥 Pre-merge checks | ✅ 5 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is detailed, but it does not follow the required template and omits the Issue section and task checklist. Add the required Issue, Approach, and Tasks sections from the template, and include the issue this PR closes plus the checklist items.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the change set and uses the required fix: prefix.
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.
Security Check ✅ Passed Multipart uploads use Apollo CSRF prevention, and filenames/paths are normalized and validated before use; no obvious vuln pattern introduced.
Engage In Review Feedback ✅ Passed coratgerl replied inline to CodeRabbit review comments, referenced fixes in commit 7b2bc6e, and re-requested review before approval.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ast-grep (0.44.0)
spec/ParseFile.spec.js

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.

@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/GraphQL/loaders/filesMutations.js`:
- Around line 41-53: The response handling treats 3xx as success and uses a
nonexistent Parse.error constant in the catch; change the status check to reject
for any non-2xx (use if (res.statusCode < 200 || res.statusCode >= 300)) and
update the catch to capture the thrown error and reject with a real Parse.Error
code constant (e.g., use Parse.Error.FILE_SAVE_ERROR or another appropriate
Parse.Error.* code) so the reject call uses new
Parse.Error(Parse.Error.FILE_SAVE_ERROR, errorOrData) and includes the caught
error details; locate these changes around the parsedData/res.statusCode
handling and the catch that currently calls reject(new Parse.Error(Parse.error,
data)).
🪄 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: a99d2150-8ad0-460f-ac62-c9b90904bbd1

📥 Commits

Reviewing files that changed from the base of the PR and between 828d0e0 and 2b87ddb.

📒 Files selected for processing (9)
  • package-lock.json
  • package.json
  • spec/ParseGraphQLServer.spec.js
  • src/Adapters/Files/FilesAdapter.js
  • src/GraphQL/ParseGraphQLSchema.js
  • src/GraphQL/ParseGraphQLServer.js
  • src/GraphQL/helpers/graphqlUpload.js
  • src/GraphQL/loaders/defaultGraphQLTypes.js
  • src/GraphQL/loaders/filesMutations.js

Comment thread src/GraphQL/loaders/filesMutations.js Outdated
@codecov

codecov Bot commented May 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.20000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 92.73%. Comparing base (cce91e5) to head (026ae5d).
⚠️ Report is 1 commits behind head on alpha.

Files with missing lines Patch % Lines
src/Adapters/Files/FilesAdapter.js 96.66% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##            alpha   #10478      +/-   ##
==========================================
+ Coverage   92.66%   92.73%   +0.06%     
==========================================
  Files         193      194       +1     
  Lines       16981    17085     +104     
  Branches      248      248              
==========================================
+ Hits        15736    15844     +108     
+ Misses       1224     1220       -4     
  Partials       21       21              

☔ 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.

Reject non-2xx responses from the internal /files handoff and use
Parse.Error.FILE_SAVE_ERROR when the response body is not valid JSON.
Add regression tests for accented filenames in FilesController and Parse.File.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coratgerl

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
✅ Actions performed

Full review triggered.

@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/Adapters/Files/FilesAdapter.js`:
- Line 115: Filename normalization is missing: validateFilename only tests the
regex but never normalizes, causing NFC/NFD mismatches across storage and
lookups. Normalize filenames (and each path segment) to a consistent Unicode
form (e.g., NFC) at the point the storage key is finalized and always pass that
normalized value into GridFS operations; update FilesAdapter (add or change
validateFilename to return/produce a normalized filename or add a new
normalizeFilename helper), ensure FilesRouter/FilesController call that
normalizer before upload/download/delete/metadata, and make GridFSBucketAdapter
use the normalized name for openUploadStream(filename),
openDownloadStreamByName, bucket.find({ filename }), delete, and any other
filename lookups.
🪄 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: bfd78a56-4652-4ed2-b00c-ac8ba20f9045

📥 Commits

Reviewing files that changed from the base of the PR and between 828d0e0 and 063b85c.

📒 Files selected for processing (11)
  • package-lock.json
  • package.json
  • spec/FilesController.spec.js
  • spec/ParseFile.spec.js
  • spec/ParseGraphQLServer.spec.js
  • src/Adapters/Files/FilesAdapter.js
  • src/GraphQL/ParseGraphQLSchema.js
  • src/GraphQL/ParseGraphQLServer.js
  • src/GraphQL/helpers/graphqlUpload.js
  • src/GraphQL/loaders/defaultGraphQLTypes.js
  • src/GraphQL/loaders/filesMutations.js

Comment thread src/Adapters/Files/FilesAdapter.js Outdated
Normalize file names to NFC before finalizing storage keys so uploads, reads, metadata lookups, and deletes resolve the same GridFS record across Unicode forms.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coratgerl

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
✅ Actions performed

Full review triggered.

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 26, 2026
Comment thread src/Adapters/Files/FilesAdapter.js Outdated
Comment thread src/GraphQL/loaders/filesMutations.js
Comment thread src/Adapters/Files/FilesAdapter.js
Reject non-string filenames before normalization, validate multi-segment
file paths on download/delete/metadata routes, and drop combining marks
from the filename allowlist after NFC normalization.

Co-authored-by: Cursor <cursoragent@cursor.com>

@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/Adapters/Files/FilesAdapter.js`:
- Around line 149-151: The current validation in FilesAdapter.js rejects any
pathname containing the substring '..' (using normalized.includes('..')), which
wrongly blocks filenames like "a..b.txt"; update validateFilepath (the block
using the normalized variable) to split the normalized path on '/' (and handle
leading/trailing slashes) and reject only when any path segment is exactly '..'
(e.g., segments.some(s => s === '..')), preserving valid filenames that contain
'..' inside a segment; keep existing Parse.Error and message but change the
detection logic accordingly.
🪄 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: 5ffaa707-9a79-4f67-ac8d-b56a7b1ebae8

📥 Commits

Reviewing files that changed from the base of the PR and between 8193231 and 7b2bc6e.

📒 Files selected for processing (5)
  • spec/FileNameNormalization.spec.js
  • spec/FilesController.spec.js
  • src/Adapters/Files/FilesAdapter.js
  • src/Controllers/FilesController.js
  • src/Routers/FilesRouter.js

Comment thread src/Adapters/Files/FilesAdapter.js Outdated
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 26, 2026
@mtrezza

mtrezza commented May 27, 2026

Copy link
Copy Markdown
Member

Could you check codecov?

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 28, 2026
@Moumouls

Moumouls commented Jun 2, 2026

Copy link
Copy Markdown
Member

@coratgerl check the failing test on Node 20

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
spec/ParseGraphQLServer.spec.js (1)

7515-7519: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Tighten the GraphQL response guard.

not.toBeNull() still passes when createFile is undefined, and the early return then skips the real assertions. That means a malformed empty 200 response can slip through as a passing test.

Proposed fix
             expect(result.errors).toBeUndefined();
-            expect(result.data?.createFile).not.toBeNull();
-            if (result.errors || !result.data?.createFile) {
+            expect(result.data?.createFile).toBeDefined();
+            if (!result.data?.createFile) {
+              fail(`Unexpected GraphQL response: ${JSON.stringify(result)}`);
               return;
             }
🤖 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 `@spec/ParseGraphQLServer.spec.js` around lines 7515 - 7519, The test currently
uses expect(result.data?.createFile).not.toBeNull(), which still allows
undefined and lets malformed empty success responses pass; change the assertion
to expect(result.data?.createFile).toBeDefined() (or use not.toBeUndefined())
and tighten the early-return guard to check for undefined specifically (e.g., if
(result.errors || result.data?.createFile === undefined) return;) so the test
fails when createFile is missing or undefined; update references to result and
result.data?.createFile accordingly.
🤖 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.

Outside diff comments:
In `@spec/ParseGraphQLServer.spec.js`:
- Around line 7515-7519: The test currently uses
expect(result.data?.createFile).not.toBeNull(), which still allows undefined and
lets malformed empty success responses pass; change the assertion to
expect(result.data?.createFile).toBeDefined() (or use not.toBeUndefined()) and
tighten the early-return guard to check for undefined specifically (e.g., if
(result.errors || result.data?.createFile === undefined) return;) so the test
fails when createFile is missing or undefined; update references to result and
result.data?.createFile accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 442ca129-a4df-4728-b3f8-1a921407e744

📥 Commits

Reviewing files that changed from the base of the PR and between dfdaae4 and 3cfd9e0.

📒 Files selected for processing (7)
  • package-lock.json
  • package.json
  • spec/ParseGraphQLSchema.spec.js
  • spec/ParseGraphQLServer.spec.js
  • src/Controllers/FilesController.js
  • src/GraphQL/ParseGraphQLSchema.js
  • src/Routers/FilesRouter.js
🚧 Files skipped from review as they are similar to previous changes (4)
  • package.json
  • package-lock.json
  • src/Controllers/FilesController.js
  • src/Routers/FilesRouter.js

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 2, 2026
@coratgerl

Copy link
Copy Markdown
Contributor Author

@mtrezza If you have the time can you check this one ? With @Moumouls we have a production issue because of that.

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.

3 participants