Skip to content

feat: Add Parse.File.url validation with config fileUpload.allowedFileUrlDomains against SSRF attacks#10044

Merged
mtrezza merged 7 commits intoparse-community:alphafrom
mtrezza:fix/ssrf
Feb 7, 2026
Merged

feat: Add Parse.File.url validation with config fileUpload.allowedFileUrlDomains against SSRF attacks#10044
mtrezza merged 7 commits intoparse-community:alphafrom
mtrezza:fix/ssrf

Conversation

@mtrezza
Copy link
Member

@mtrezza mtrezza commented Feb 7, 2026

Pull Request

Issue

A developer can introduce a SSRF vulnerability if an attacker passes a crafted Parse.File with an arbitrary URL (e.g., {__type: "File", name: "file.txt", url: "http://malicious.example.com"}) as a Cloud Function parameter or save it to the database in a Parse.Object field of type Object or Array. When cloud code or clients call Parse.File.getData(), the Parse JS SDK makes an HTTP request to the attacker-controlled URL, leaking the server/client IP and potentially accessing internal services.

Note that this is not a vulnerability of Parse Server itself, as developers should not trust client-provided data they don't fully control, but instead validate it. This PR introduces a feature that allows to easily validate the Parse.File.url whenever JSON data is instantiated as Parse.File, specifically in the following scenarios:

  • Parse.File as item in Parse.Object field of type Array.
  • Parse.File as key value in Parse.Object field of type Object.
  • Parse.File as parameter in a Cloud Function call.

This is not a breaking change, as the default configuration in this PR allows any URL. However, the default is deprecated and Parse Server logs a warning about a future change of the default value.

Summary by CodeRabbit

  • New Features

    • Configurable file URL domain restrictions via fileUpload.allowedFileUrlDomains; enforced on file creation/update across REST, GraphQL, and cloud function paths. Includes validation of config values.
  • Tests

    • Extensive coverage for URL validation: wildcards, exact/case-insensitive matches, nested/array fields, invalid URLs, and SSRF scenarios; deprecation-related tests added.
  • Documentation

    • Added docs and config entry with examples, defaults, and usage guidance.
  • Chores

    • Added deprecation notice for the new option.

@parse-github-assistant
Copy link

parse-github-assistant bot commented Feb 7, 2026

🚀 Thanks for opening this pull request!

@parseplatformorg
Copy link
Contributor

parseplatformorg commented Feb 7, 2026

Snyk checks have passed. No issues have been found so far.

Status Scanner Image Critical Image High Image Medium Image Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@coderabbitai
Copy link

coderabbitai bot commented Feb 7, 2026

📝 Walkthrough

Walkthrough

Adds domain-whitelisting for file URLs: new FileUrlValidator validates File.url entries against fileUpload.allowedFileUrlDomains; config, options, docs, and types added; validation invoked in database create/update, GraphQL mutations, and cloud function routing; extensive tests added.

Changes

Cohort / File(s) Summary
Core Validation Module
src/FileUrlValidator.js
New module exporting validateFileUrl and validateFileUrlsInObject to validate single URLs and recursively validate File objects (supports exact and *.domain wildcard matching, case-insensitive).
Configuration & Types
src/Options/Definitions.js, src/Options/docs.js, src/Options/index.js, types/Options/index.d.ts, src/Config.js
Added allowedFileUrlDomains FileUpload option (env var, default ['*']), documented and typed; Config validates option is an array of non-empty strings when provided.
Integration Points
src/Controllers/DatabaseController.js, src/Routers/FunctionsRouter.js, src/GraphQL/transformers/mutation.js
Invoke FileUrlValidator before persisting or returning File objects: database create/update flows, cloud function File parsing, and GraphQL mutation file handling. Validation failures are normalized to Parse.Error (FILE_SAVE_ERROR) where applicable.
Tests
spec/FileUrlValidator.spec.js, spec/ParseFile.spec.js, spec/ParseGraphQLServer.spec.js, spec/Deprecator.spec.js
Extensive tests for validator behavior (null/empty, wildcards, subdomains, case-insensitivity, invalid URLs) and SSRF scenarios across REST, GraphQL, and cloud functions; added deprecation-related tests for the new option.
Docs & README
README.md
New "Restricting File URL Domains" section and TOC entries explaining configuration, patterns (*, [], *.domain), and examples.
Deprecations
src/Deprecator/Deprecations.js
Added a deprecation entry for fileUpload.allowedFileUrlDomains with guidance and new default note.
Minor
src/ParseServer.ts
Small syntax modernization in a catch clause; no behavioral change.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client
  participant API as API (REST / GraphQL / CloudFunction)
  participant Validator as FileUrlValidator
  participant DB as DatabaseController

  Client->>API: send create/update with object containing File.url
  API->>Validator: validateFileUrlsInObject / validateFileUrl
  alt validation passes
    Validator-->>API: OK
    API->>DB: persist object
    DB-->>API: saved
    API-->>Client: success response
  else validation fails
    Validator-->>API: throws Parse.Error (FILE_SAVE_ERROR)
    API-->>Client: error response
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description addresses the Issue section detailing the SSRF vulnerability context, but the Approach section is missing details about implementation specifics, and Tasks checklist is incomplete. Expand the Approach section to describe key implementation details (validation logic, integration points), and complete the Tasks checklist to confirm all items have been addressed.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately summarizes the main change: adding Parse.File.url validation with a new config option to prevent SSRF attacks.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

@codecov
Copy link

codecov bot commented Feb 7, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.55%. Comparing base (9e07ca6) to head (8793e8a).
⚠️ Report is 2 commits behind head on alpha.

Additional details and impacted files
@@            Coverage Diff             @@
##            alpha   #10044      +/-   ##
==========================================
+ Coverage   92.53%   92.55%   +0.02%     
==========================================
  Files         190      191       +1     
  Lines       15521    15573      +52     
  Branches      176      176              
==========================================
+ Hits        14362    14414      +52     
  Misses       1147     1147              
  Partials       12       12              

☔ View full report in Codecov by Sentry.
📢 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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/Config.js`:
- Around line 553-557: The config allows fileUpload.allowedFileUrlDomains to be
an array but doesn't validate element types, which later causes a toLowerCase
TypeError in FileUrlValidator; update the validation where
fileUpload.allowedFileUrlDomains is set (near
FileUploadOptions.allowedFileUrlDomains.default) to ensure every entry is a
non-empty string (e.g., iterate the array and throw a clear error if any element
is not typeof 'string' or is an empty string) so callers get an immediate,
descriptive config error before FileUrlValidator runs.

In `@src/Options/index.js`:
- Around line 633-635: Add the missing allowedFileUrlDomains property to the
FileUploadOptions TypeScript interface in types/Options/index.d.ts so TypeScript
consumers see the option; declare it with the same shape used elsewhere (e.g.,
allowedFileUrlDomains?: string[]), matching how it appears in
src/Options/index.js, src/Options/Definitions.js, and src/Options/docs.js, and
run type checks to ensure no other type changes are required.
🧹 Nitpick comments (5)
spec/FileUrlValidator.spec.js (1)

75-83: Consider adding tests for IP-based URLs to strengthen SSRF coverage.

The partial hostname match test is excellent for preventing domain suffix attacks. However, SSRF attacks commonly use IP addresses (e.g., http://127.0.0.1/file, http://169.254.169.254/metadata) to target internal services. Consider adding a test to verify that IP-based URLs are correctly handled when allowedFileUrlDomains only contains domain names.

src/GraphQL/transformers/mutation.js (1)

100-103: Inline require inside hot path — consider hoisting to module scope.

The require('../../FileUrlValidator') is called on every file mutation. While Node.js caches require results, the repeated cache lookup and destructuring inside the function body is unnecessary. Both this file and FunctionsRouter.js use this pattern — consider hoisting the import to the top of the file for clarity and consistency with the existing ES module imports (line 1–4).

Proposed fix

At the top of the file (after line 4):

import { validateFileUrl } from '../../FileUrlValidator';

Then simplify lines 100–103:

       if (file.url) {
-        const { validateFileUrl } = require('../../FileUrlValidator');
         validateFileUrl(file.url, config);
       }
spec/ParseGraphQLServer.spec.js (1)

10257-10258: Remove redundant schema cache clearing.

Line 10257 already clears the schema cache via resetGraphQLCache(), so Line 10258 is redundant and can be dropped for clarity.

♻️ Proposed cleanup
-            await resetGraphQLCache();
-            await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear();
+            await resetGraphQLCache();
src/Controllers/DatabaseController.js (2)

502-507: Prefer a top-level import over a dynamic require inside the hot path.

Every other dependency in this file uses a static ES import. The inline require('../FileUrlValidator') re-resolves the module on every update (and create) call. Move it to the top with the other imports:

♻️ Suggested change

Add at the top of the file (e.g. after line 14):

import { validateFileUrlsInObject } from '../FileUrlValidator';

Then simplify both call sites:

     try {
-      const { validateFileUrlsInObject } = require('../FileUrlValidator');
       validateFileUrlsInObject(update, this.options);
     } catch (error) {

502-507: Extract the shared validation + error-wrapping into a private helper to reduce duplication.

The try/catch/require/reject block is duplicated verbatim in update (lines 502-507) and create (lines 845-850). A small helper keeps both paths in sync:

♻️ Example helper
_validateFileUrls(obj) {
  try {
    validateFileUrlsInObject(obj, this.options);
  } catch (error) {
    throw error instanceof Parse.Error
      ? error
      : new Parse.Error(Parse.Error.FILE_SAVE_ERROR, error.message || error);
  }
}

Then in both update and create:

-    try {
-      const { validateFileUrlsInObject } = require('../FileUrlValidator');
-      validateFileUrlsInObject(update, this.options);
-    } catch (error) {
-      return Promise.reject(error instanceof Parse.Error ? error : new Parse.Error(Parse.Error.FILE_SAVE_ERROR, error.message || error));
-    }
+    try {
+      this._validateFileUrls(update);
+    } catch (error) {
+      return Promise.reject(error);
+    }

Also applies to: 845-850

@mtrezza mtrezza changed the title feat: Parse.File.url validation with config fileUpload.allowedFileUrlDomains feat: Add Parse.File.url validation with config fileUpload.allowedFileUrlDomains against SSRF attacks Feb 7, 2026
coderabbitai[bot]
coderabbitai bot previously approved these changes Feb 7, 2026
@mtrezza mtrezza merged commit 4c9c948 into parse-community:alpha Feb 7, 2026
23 checks passed
parseplatformorg pushed a commit that referenced this pull request Feb 7, 2026
# [9.3.0-alpha.3](9.3.0-alpha.2...9.3.0-alpha.3) (2026-02-07)

### Features

* Add `Parse.File.url` validation with config `fileUpload.allowedFileUrlDomains` against SSRF attacks ([#10044](#10044)) ([4c9c948](4c9c948))
@parseplatformorg
Copy link
Contributor

🎉 This change has been released in version 9.3.0-alpha.3

@parseplatformorg parseplatformorg added the state:released-alpha Released as alpha version label Feb 7, 2026
@mtrezza mtrezza deleted the fix/ssrf branch February 7, 2026 20:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state:released-alpha Released as alpha version

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants