Skip to content

feat: Add header aliasing system for Parse headers#10401

Open
Moumouls wants to merge 6 commits intoparse-community:alphafrom
Moumouls:moumouls/session-token-rename
Open

feat: Add header aliasing system for Parse headers#10401
Moumouls wants to merge 6 commits intoparse-community:alphafrom
Moumouls:moumouls/session-token-rename

Conversation

@Moumouls
Copy link
Copy Markdown
Member

@Moumouls Moumouls commented Apr 5, 2026

Pull Request

Issue

Security hardening through header masking:
According to OWASP recommendations, it is best practice to mask or obfuscate technology-specific details whenever possible. For example, headers such as X-Parse-Session-Token and X-Parse-Application-Id clearly reveal the backend technology stack and should be renamed or aliased to reduce exposure.

Session management in micro frontend architectures or multi parse-server under one domain:
In a micro frontend setup with multiple Parse Server instances under the same domain, developers may not be able to use HttpOnly cookies. As a result, they might be forced to rely on alternative methods, often less secure, such as localStorage or sessionStorage to manage session tokens across instances.
This feature allows developers to configure alias names for session headers and application IDs, helping maintain flexibility while improving security.

Approach

Alias system: redirects the value of a custom (aliased) header to the corresponding internally recognized Parse header.

Made with Composer 2 and Codex 5.3 High + manually reduced implementation complexity

Tasks

  • Add tests
  • Add changes to documentation (guides, repository pages, code comments)
  • Add security check
  • Add new Parse Error codes to Parse JS SDK

Summary by CodeRabbit

  • New Features

    • Added configurable header aliases across REST, GraphQL, CORS and auth flows; CSRF header whitelist now includes safe aliases and excludes unsafe headers.
  • Bug Fixes

    • Canonical headers take precedence when both canonical and aliased headers are present; alias-based master key authentication supported; middleware now tolerates missing app config or null alias settings.
  • Tests

    • Extensive unit and integration tests for alias validation, middleware behavior, GraphQL/REST flows, CORS headers, and precedence rules.
  • Documentation

    • Options, defaults, and docs updated to document the new headerAliases config and env var usage.

@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 feat: add header aliasing system for Parse headers feat: Add header aliasing system for Parse headers Apr 5, 2026
@parse-github-assistant
Copy link
Copy Markdown

parse-github-assistant bot commented Apr 5, 2026

🚀 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 rather than using review cycles to iteratively fix low-quality code.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 5, 2026

📝 Walkthrough

Walkthrough

Adds support for configurable header aliases: validates config, rewrites incoming request headers to canonical Parse names via new middleware, includes aliases in CORS and GraphQL CSRF headers, and adds tests covering middleware, REST, and GraphQL flows.

Changes

Cohort / File(s) Summary
Configuration & Types
src/Config.js, src/Options/Definitions.js, src/Options/docs.js, src/Options/index.js, types/Options/index.d.ts, src/defaults.js
Added headerAliases option (default {}), JSDoc/type and env var support, and Config.validateHeaderAliases to validate alias shape and collisions.
Core Middleware
src/middlewares.js, src/ParseServer.ts
Added header utilities (getHeaderList, mergeHeaders, getHeaderAliases), handleHeaderAliases(appId) middleware to rewrite request headers, and updated allowCrossDomain to include alias headers. Middleware registered early (before auth/header parsing).
GraphQL Integration
src/GraphQL/ParseGraphQLServer.js
Exported getCSRFRequestHeaders(headerAliases) to compute Apollo CSRF header whitelist from canonical header + aliases (with blocklist filtering) and registered handleHeaderAliases before Parse header/session handling.
Tests — Middleware / Auth / Robustness
spec/Middlewares.spec.js, spec/HeaderAliasesValidation.spec.js
New tests verifying alias mapping into canonical headers, precedence rules (canonical > alias), master-key via alias, robustness when app/config missing, and validator unit tests for collision/error cases.
Tests — GraphQL & CSRF
spec/ParseGraphQLServer.spec.js
Added tests for getCSRFRequestHeaders, GraphQL middleware registration order, header rewriting before handleParseHeaders, and GraphQL session-token alias integration.
Tests — REST
spec/rest.spec.js
Integration tests for REST endpoint access using aliased X-Parse-Application-Id and aliased X-Parse-Session-Token, with server reconfiguration preserved.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Proxy as Parse Server (Express)
    participant AliasMW as handleHeaderAliases
    participant HeaderMW as handleParseHeaders
    participant AuthMW as handleParseAuth
    participant GraphQL as Apollo Server

    Client->>Proxy: HTTP request with alias header (e.g., X-App-Id)
    Proxy->>AliasMW: invoke middleware (load app config)
    AliasMW->>AliasMW: map alias → canonical header, rewrite req.headers
    AliasMW-->>Proxy: next()
    Proxy->>HeaderMW: handleParseHeaders reads canonical headers
    HeaderMW-->>AuthMW: parsed header info (appId, sessionToken)
    AuthMW->>AuthMW: validate token / master key
    Proxy->>GraphQL: request reaches GraphQL server with canonical headers
    GraphQL->>GraphQL: CSRF header whitelist includes aliases
    GraphQL-->>Client: response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • mtrezza
🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (1 warning, 2 inconclusive)

Check name Status Explanation Resolution
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.
Security Check ❓ Inconclusive Code files referenced in PR summary (src/middlewares.js, src/Config.js, etc.) are not accessible in the repository. Ensure correct PR branch is checked out and verify that all referenced source files exist before conducting security review.
Description check ❓ Inconclusive The PR description is mostly complete with sections for Issue, Approach, and Tasks. However, the Issue section only contains a brief title followed by explanatory text without a specific issue number or link, and the approach lacks implementation details about the alias system mechanics. Add a specific issue number or GitHub issue link in the Issue section to clarify the related work. Additionally, provide more concrete implementation details in the Approach section (e.g., how aliases are registered and applied in the middleware chain, or reference the changes made in middlewares.js and Config.js).
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: Add header aliasing system for Parse headers' follows the required 'feat:' prefix and clearly describes the main feature being added.
Engage In Review Feedback ✅ Passed PR demonstrates engagement with review feedback through comprehensive implementation of security fixes, null-safety guards, validation functions, and extensive test coverage across multiple test files.

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

@Moumouls
Copy link
Copy Markdown
Member Author

Moumouls commented Apr 5, 2026

@coderabbitai do you see security issues ? Or strong implementation gaps ?

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 5, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get PR details and diff
gh pr view 10401 --repo parse-community/parse-server --json title,body,files

# Get the diff of the PR
gh pr diff 10401 --repo parse-community/parse-server

Length of output: 24429


@Moumouls Here's my security and implementation review of PR #10401:


🔴 Security Issues & Implementation Gaps

1. Canonical Header Overwrite — Behaviour Contradicts Documentation (Critical Bug)

The documented behaviour states: "If the canonical header is not present in a request, Parse Server uses the first matching alias."

However, applyHeaderAliases overwrites the canonical header unconditionally:

// src/middlewares.js
Object.entries(req.headers).forEach(([header, value]) => {
  const targetHeader = indexHeaderByAlias[header.toLowerCase()];
  if (targetHeader) {
    req.headers[targetHeader] = value; // ← always overwrites, no guard
  }
});

If a client sends both X-Parse-Session-Token: tokenA and X-Session-Token-Alias: tokenB, tokenB silently wins. This means an alias header can replace a legitimately set canonical value, which is a security concern when both headers are present. The fix is straightforward:

if (targetHeader && !req.headers[targetHeader]) {
  req.headers[targetHeader] = value;
}

2. Null Safety Crash in applyHeaderAliases

handleHeaderAliases calls:

applyHeaderAliases(req, config?.headerAliases);

If Config.get(appId, ...) returns null/undefined (e.g. during startup races or unknown appId), then config?.headerAliases is undefined, and inside applyHeaderAliases:

Object.entries(headerAliases) // TypeError: Cannot convert undefined or null to object

This crashes the request. A null guard is missing:

function applyHeaderAliases(req, headerAliases) {
  if (!headerAliases) return; // ← needs this guard
  // ...
}

Similarly, getCSRFRequestHeaders calls getHeaderAliases(headerAliases, ...) — if headerAliases is undefined, headerAliases[canonicalHeader] also throws.


3. No CRLF/Special Character Validation on Alias Names (Header Injection Risk)

validateHeaderAliases only rejects empty strings but does not sanitize for \r\n or : characters. Alias names are injected directly into the Access-Control-Allow-Headers response header:

// allowCrossDomain in middlewares.js
const allowHeaders = mergeHeaders(
  DEFAULT_ALLOWED_HEADERS,
  mergeHeaders(...Object.values(config?.headerAliases || {})), // ← unsanitized alias names
  config?.allowHeaders
).join(', ');
res.header('Access-Control-Allow-Headers', allowHeaders);

A misconfigured alias like "X-Alias\r\nX-Injected: evil" would enable HTTP response header injection. Alias names should be validated against a safe pattern (e.g., /^[a-zA-Z0-9\-]+$/).


4. No Allowlist for Canonical Headers

validateHeaderAliases accepts any string as the canonical key — including Authorization, Host, or Cookie. There is no guard restricting aliasing to known Parse headers (X-Parse-*). Aliasing infrastructure or proxy headers to Parse headers can have unpredictable or dangerous consequences.


5. Duplicate Alias Across Multiple Canonical Headers (Silent Conflict)

If two canonical headers share the same alias:

{
  "X-Parse-Application-Id": ["X-Shared"],
  "X-Parse-Master-Key":     ["X-Shared"]  // ← same alias!
}

Object.fromEntries silently keeps only the last entry. This leads to a silent misconfiguration that is not caught by validateHeaderAliases. A duplicate alias check should be added.


6. Master Key Aliasing Has No Additional Warning or Restriction

The implementation allows aliasing X-Parse-Master-Key without any special-casing. This could:

  • Confuse WAFs, security monitoring, or audit tools that inspect for the canonical master key header
  • Allow using obscure header names to pass the master key, which is harder to detect in logs

At minimum, this should be documented as a high-risk configuration option.


7. Security Check Unchecked in PR

The PR's own checklist has "Add security check" unchecked. Given the above findings — particularly header injection and overwrite issues — this check should be completed before merge.


Summary Table

# Severity Type Description
1 🔴 High Bug + Security Alias overwrites canonical header even when already present
2 🔴 High Bug Object.entries(undefined) crash when config is null
3 🟠 Medium Security No CRLF sanitization on alias names → header injection
4 🟠 Medium Security No allowlist for canonical header keys
5 🟡 Low Bug Duplicate alias across canonical headers silently ignored
6 🟡 Low Security No warning for master key aliasing
7 🟡 Low Process Security check PR task is unchecked

🧠 Learnings used
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 10158
File: spec/CloudCode.spec.js:4473-4487
Timestamp: 2026-03-09T19:00:02.915Z
Learning: In parse-community/parse-server, the `responseHeaders` object exposed to `afterFind` file hooks (Parse.Cloud.afterFind(Parse.File, ...)) is intentionally fully mutable by the server administrator. Admins can add, modify, or delete headers — including default security headers like `X-Content-Type-Options: nosniff` — via `req.responseHeaders`. Removing a security header is treated as an explicit, conscious decision by the person who also holds the master key. Parse Server provides secure defaults but does not enforce them as non-overridable. This is consistent with the `forceDownload` pattern that also gives Cloud Code full control over file response behavior.

Learnt from: mtrezza
Repo: parse-community/parse-server PR: 10394
File: src/Routers/FilesRouter.js:96-105
Timestamp: 2026-04-03T18:40:52.899Z
Learning: In parse-community/parse-server `src/Routers/FilesRouter.js` (PR `#10394`), the `initInfo` middleware uses `!sessionToken` (not `== null`) to gate the public-access fast path. `req.get('X-Parse-Session-Token')` returns `undefined` when the header is absent and `''` when the header is present but empty. Both values are trivially invalid session tokens; treating them identically (skipping `handleParseSession`) is intentional and correct. Using `== null` would send empty strings to `handleParseSession` for a guaranteed-failing DB lookup, which is strictly worse behavior.

Learnt from: mtrezza
Repo: parse-community/parse-server PR: 10237
File: src/Routers/PagesRouter.js:552-573
Timestamp: 2026-03-18T17:53:35.669Z
Learning: In parse-community/parse-server, `src/Routers/PagesRouter.js`: the `encodeURIComponent` call in `composePageParamHeaders` is pre-existing behavior extracted from inline code in `pageResponse` and `redirectResponse`. The potential URIError on malformed Unicode (lone surrogates) when `encodePageParamHeaders` is enabled is a known pre-existing issue, not introduced by the CRLF sanitization refactor. Do not flag it as a new issue in PRs that only refactor or consolidate this call.

Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-08T13:46:04.940Z
Learning: When reviewing Parse Server PRs that add new features, always check whether the feature is documented in the README.md file, though for new Parse Server options this is optional rather than required.

Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-17T15:02:48.786Z
Learning: For Parse Server PRs, always suggest an Angular commit convention PR title that would make a meaningful changelog entry for developers. Update the PR title suggestion on every commit. The format should be: type(scope): description. Common types include feat, fix, perf, refactor, docs, test, chore. The scope should identify the subsystem (e.g., graphql, rest, push, security). The description should be action-oriented and clearly convey the change's impact to developers.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 5, 2026

Codecov Report

❌ Patch coverage is 93.67089% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.50%. Comparing base (d507575) to head (dc72f41).
⚠️ Report is 1 commits behind head on alpha.

Files with missing lines Patch % Lines
src/Config.js 87.17% 5 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##            alpha   #10401   +/-   ##
=======================================
  Coverage   92.49%   92.50%           
=======================================
  Files         192      192           
  Lines       16732    16806   +74     
  Branches      229      229           
=======================================
+ Hits        15477    15547   +70     
- Misses       1233     1237    +4     
  Partials       22       22           

☔ 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
Copy Markdown

@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: 5

🧹 Nitpick comments (2)
spec/rest.spec.js (1)

1741-1788: Add one precedence test for canonical + alias sent together.

These tests validate alias support well; please add a case that sends both canonical and alias headers and asserts canonical wins (or the intended policy), so this behavior is locked down.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@spec/rest.spec.js` around lines 1741 - 1788, Add a new test in the same "rest
header aliases" describe block that calls reconfigureServer with headerAliases
(same shape used in existing tests), then sends a request via the existing
request helper to an endpoint (e.g., GET /users/me or GET /schemas) with both
the canonical header (e.g., 'X-Parse-Application-Id' or 'X-Parse-Session-Token')
and its alias (e.g., 'X-App-Id' or 'X-Session-Token-Alias') present but
containing different values, and assert that the server uses the canonical value
(for example, create a user via Parse.User.signUp, send both
'X-Parse-Session-Token' with the real token and 'X-Session-Token-Alias' with a
bogus token, call request({ url: `${Parse.serverURL}/users/me`, ... }) and
expect response.data.objectId to equal the signed-up user's id), then finally
call reconfigureServer() in a finally block to restore defaults. Ensure the test
name clearly states it checks canonical-over-alias precedence and reuse the
helpers reconfigureServer, request, and Parse from the existing tests.
spec/Middlewares.spec.js (1)

431-481: Add a canonical-wins regression for aliased headers.

These tests cover the alias-only path, but they do not pin the more security-sensitive case where both the canonical X-Parse-* header and an alias are present. Please add one regression that proves the canonical header wins; that will catch accidental overwrites in handleHeaderAliases().

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@spec/Middlewares.spec.js` around lines 431 - 481, Add a regression test that
ensures canonical X-Parse-* headers take precedence over configured aliases by
creating a test (alongside existing ones) that sets both the canonical header
(e.g., 'x-parse-application-id' or 'x-parse-session-token' or
'x-parse-master-key') and its alias (e.g., 'x-app-id' / 'x-session-token-alias'
/ 'x-master-key-alias') to different values, invoke
middlewares.handleHeaderAliases(...) then invoke
middlewares.handleParseHeaders(...) or middlewares.handleParseAuth(...) as
appropriate, and assert that fakeReq.headers['x-parse-application-id'] (or
fakeReq.info.appId / fakeReq.info.sessionToken / fakeReq.auth.isMaster) reflects
the canonical header value (not the alias); use the same AppCachePut pattern and
header alias config as existing tests and mirror their async/callback invocation
style so the regression is enforced.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Config.js`:
- Around line 727-748: In validateHeaderAliases, after the existing type checks
in the static method validateHeaderAliases, normalize every canonicalHeader and
each alias by trimming and lowercasing, then detect and reject any collisions:
(1) an alias that normalizes to the same string as any other alias or
canonicalHeader (including its own canonicalHeader) should cause a thrown error,
and (2) any duplicate normalized alias within a single aliases array should be
rejected; use the identifiers canonicalHeader and aliases to build clear error
messages that indicate which canonical header and alias collided and throw (not
silently accept) to prevent multiple Parse headers mapping to the same effective
header.

In `@src/GraphQL/ParseGraphQLServer.js`:
- Around line 158-163: The Apollo CSRF whitelist currently includes header
aliases from parseServer.config.headerAliases via getCSRFRequestHeaders(), which
can allow CORS-safelisted names (e.g., Accept) and weaken protection; update
getCSRFRequestHeaders() to either (a) only return canonical header names (no
aliases) or (b) filter out any alias that matches a predefined set of
CORS-safelisted header names before returning (include names like Accept,
Accept-Language, Content-Language, Content-Type and other safelisted request
headers), and ensure the ApolloServer config uses that filtered list for
csrfPrevention.requestHeaders; additionally move or delay the
handleHeaderAliases middleware so it runs after Apollo's CSRF check (or remove
alias handling from the pre-CSRF path) to avoid aliasing before validation.

In `@src/middlewares.js`:
- Around line 86-116: getHeaderAliases and applyHeaderAliases assume
headerAliases is an object and will throw if undefined; in handleHeaderAliases,
ensure the config?.headerAliases value is null-safe before passing it (e.g.,
default to {} or guard and skip application). Update handleHeaderAliases to call
applyHeaderAliases(req, config?.headerAliases || {}) or guard in
applyHeaderAliases/getHeaderAliases to handle non-object inputs, referencing
functions getHeaderAliases, applyHeaderAliases and the call site in
handleHeaderAliases that uses Config.get(appId, getMountForRequest(req)).
- Around line 95-110: In applyHeaderAliases, avoid letting alias headers clobber
an existing canonical header: for each canonical name in headerAliases, first
check (case-insensitively) if req.headers already contains the canonical name
and if so do nothing; otherwise iterate the configured aliases in their defined
order and, for the first alias present in req.headers (case-insensitive), copy
that value into the canonical header slot. Use lowercased lookups for
comparisons but do not overwrite an existing canonical header; ensure alias
precedence comes from headerAliases' alias array order rather than request
header iteration order.

In `@src/ParseServer.ts`:
- Line 314: The middleware currently overwrites canonical headers with aliases;
update the implementation in handleHeaderAliases/applyHeaderAliases so that for
each header alias mapping it only sets the canonical header value when the
canonical header is missing or empty (do not overwrite when request.headers has
the canonical key present), and ensure you preserve original header casing/keys
used by Parse logic; also add a unit/integration test that sends both canonical
and alias headers and asserts the canonical header value is preserved and used
for authentication/application-id resolution.

---

Nitpick comments:
In `@spec/Middlewares.spec.js`:
- Around line 431-481: Add a regression test that ensures canonical X-Parse-*
headers take precedence over configured aliases by creating a test (alongside
existing ones) that sets both the canonical header (e.g.,
'x-parse-application-id' or 'x-parse-session-token' or 'x-parse-master-key') and
its alias (e.g., 'x-app-id' / 'x-session-token-alias' / 'x-master-key-alias') to
different values, invoke middlewares.handleHeaderAliases(...) then invoke
middlewares.handleParseHeaders(...) or middlewares.handleParseAuth(...) as
appropriate, and assert that fakeReq.headers['x-parse-application-id'] (or
fakeReq.info.appId / fakeReq.info.sessionToken / fakeReq.auth.isMaster) reflects
the canonical header value (not the alias); use the same AppCachePut pattern and
header alias config as existing tests and mirror their async/callback invocation
style so the regression is enforced.

In `@spec/rest.spec.js`:
- Around line 1741-1788: Add a new test in the same "rest header aliases"
describe block that calls reconfigureServer with headerAliases (same shape used
in existing tests), then sends a request via the existing request helper to an
endpoint (e.g., GET /users/me or GET /schemas) with both the canonical header
(e.g., 'X-Parse-Application-Id' or 'X-Parse-Session-Token') and its alias (e.g.,
'X-App-Id' or 'X-Session-Token-Alias') present but containing different values,
and assert that the server uses the canonical value (for example, create a user
via Parse.User.signUp, send both 'X-Parse-Session-Token' with the real token and
'X-Session-Token-Alias' with a bogus token, call request({ url:
`${Parse.serverURL}/users/me`, ... }) and expect response.data.objectId to equal
the signed-up user's id), then finally call reconfigureServer() in a finally
block to restore defaults. Ensure the test name clearly states it checks
canonical-over-alias precedence and reuse the helpers reconfigureServer,
request, and Parse from the existing tests.
🪄 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: 985cb1fa-03eb-47f1-beed-cd7632aa1b45

📥 Commits

Reviewing files that changed from the base of the PR and between 9168e69 and a40d82c.

📒 Files selected for processing (12)
  • spec/Middlewares.spec.js
  • spec/ParseGraphQLServer.spec.js
  • spec/rest.spec.js
  • src/Config.js
  • src/GraphQL/ParseGraphQLServer.js
  • src/Options/Definitions.js
  • src/Options/docs.js
  • src/Options/index.js
  • src/ParseServer.ts
  • src/defaults.js
  • src/middlewares.js
  • types/Options/index.d.ts

Comment on lines +95 to +110
function applyHeaderAliases(req, headerAliases) {
req.headers = req.headers || {};
const indexHeaderByAlias = Object.fromEntries(
Object.entries(headerAliases)
.map(([source, aliases]) =>
aliases
.map(alias => [alias.toLowerCase(), source.toLowerCase()])
)
.flat()
);
Object.entries(req.headers).forEach(([header, value]) => {
const targetHeader = indexHeaderByAlias[header.toLowerCase()];
if (targetHeader) {
req.headers[targetHeader] = value;
}
});
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Apr 5, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Do not let aliases overwrite canonical headers.

Line 108 always copies the alias value into the canonical slot. If both headers are present, a client-controlled alias can clobber an existing canonical header, and if multiple aliases are present the winner depends on request header order instead of the configured alias order. For auth headers, that can swap the app ID or session token unexpectedly.

Proposed fix
 function applyHeaderAliases(req, headerAliases) {
   req.headers = req.headers || {};
-  const indexHeaderByAlias = Object.fromEntries(
-    Object.entries(headerAliases)
-      .map(([source, aliases]) =>
-        aliases
-          .map(alias => [alias.toLowerCase(), source.toLowerCase()])
-      )
-      .flat()
-  );
-  Object.entries(req.headers).forEach(([header, value]) => {
-    const targetHeader = indexHeaderByAlias[header.toLowerCase()];
-    if (targetHeader) {
-      req.headers[targetHeader] = value;
-    }
-  });
+  for (const [canonicalHeader, aliases] of Object.entries(headerAliases)) {
+    const canonicalKey = canonicalHeader.toLowerCase();
+    if (req.headers[canonicalKey] !== undefined) {
+      continue;
+    }
+    const matchedAlias = aliases.find(alias => req.headers[alias.toLowerCase()] !== undefined);
+    if (matchedAlias) {
+      req.headers[canonicalKey] = req.headers[matchedAlias.toLowerCase()];
+    }
+  }
 }
📝 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
function applyHeaderAliases(req, headerAliases) {
req.headers = req.headers || {};
const indexHeaderByAlias = Object.fromEntries(
Object.entries(headerAliases)
.map(([source, aliases]) =>
aliases
.map(alias => [alias.toLowerCase(), source.toLowerCase()])
)
.flat()
);
Object.entries(req.headers).forEach(([header, value]) => {
const targetHeader = indexHeaderByAlias[header.toLowerCase()];
if (targetHeader) {
req.headers[targetHeader] = value;
}
});
function applyHeaderAliases(req, headerAliases) {
req.headers = req.headers || {};
for (const [canonicalHeader, aliases] of Object.entries(headerAliases)) {
const canonicalKey = canonicalHeader.toLowerCase();
if (req.headers[canonicalKey] !== undefined) {
continue;
}
const matchedAlias = aliases.find(alias => req.headers[alias.toLowerCase()] !== undefined);
if (matchedAlias) {
req.headers[canonicalKey] = req.headers[matchedAlias.toLowerCase()];
}
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/middlewares.js` around lines 95 - 110, In applyHeaderAliases, avoid
letting alias headers clobber an existing canonical header: for each canonical
name in headerAliases, first check (case-insensitively) if req.headers already
contains the canonical name and if so do nothing; otherwise iterate the
configured aliases in their defined order and, for the first alias present in
req.headers (case-insensitive), copy that value into the canonical header slot.
Use lowercased lookups for comparisons but do not overwrite an existing
canonical header; ensure alias precedence comes from headerAliases' alias array
order rather than request header iteration order.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Check to see whether the issue is addressed.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@mtrezza, the primary concern (aliases unconditionally overwriting canonical headers) is addressed in the current file. The guard !req.headers[targetHeader] was added, so a canonical header already present in the request is never overwritten by an alias.

However, one sub-point from the original comment is not yet resolved: alias precedence still depends on req.headers iteration order rather than the configured alias array order. The current implementation iterates over request headers and stops at the first alias hit in that order:

// Current: iterates req.headers — winner depends on request header iteration order
Object.entries(req.headers).forEach(([header, value]) => {
  const targetHeader = indexHeaderByAlias[header.toLowerCase()];
  if (targetHeader && !req.headers[targetHeader]) {
    req.headers[targetHeader] = value;
  }
});

If a client sends two alias headers for the same canonical target (e.g., both X-App-Id-1 and X-App-Id-2), the winner is whichever appears first in req.headers — which is undefined/engine-dependent — rather than the first alias listed in the headerAliases config. The proposed fix addressed this by iterating the config instead:

// Proposed: iterates config — winner is first alias in the configured array
for (const [canonicalHeader, aliases] of Object.entries(headerAliases)) {
  const canonicalKey = canonicalHeader.toLowerCase();
  if (req.headers[canonicalKey] !== undefined) continue;
  const matchedAlias = aliases.find(alias => req.headers[alias.toLowerCase()] !== undefined);
  if (matchedAlias) {
    req.headers[canonicalKey] = req.headers[matchedAlias.toLowerCase()];
  }
}

Summary:

Concern Status
Alias overwrites canonical header ✅ Fixed (!req.headers[targetHeader] guard)
Alias precedence follows configured array order ❌ Still uses req.headers iteration order

Would you like to keep the remaining sub-issue for a follow-up, or apply the full proposed fix now?


🧠 Learnings used
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 10237
File: src/Routers/PagesRouter.js:552-573
Timestamp: 2026-03-18T17:53:35.669Z
Learning: In parse-community/parse-server, `src/Routers/PagesRouter.js`: the `encodeURIComponent` call in `composePageParamHeaders` is pre-existing behavior extracted from inline code in `pageResponse` and `redirectResponse`. The potential URIError on malformed Unicode (lone surrogates) when `encodePageParamHeaders` is enabled is a known pre-existing issue, not introduced by the CRLF sanitization refactor. Do not flag it as a new issue in PRs that only refactor or consolidate this call.

Learnt from: mtrezza
Repo: parse-community/parse-server PR: 10394
File: src/Routers/FilesRouter.js:96-105
Timestamp: 2026-04-03T18:40:52.899Z
Learning: In parse-community/parse-server `src/Routers/FilesRouter.js` (PR `#10394`), the `initInfo` middleware uses `!sessionToken` (not `== null`) to gate the public-access fast path. `req.get('X-Parse-Session-Token')` returns `undefined` when the header is absent and `''` when the header is present but empty. Both values are trivially invalid session tokens; treating them identically (skipping `handleParseSession`) is intentional and correct. Using `== null` would send empty strings to `handleParseSession` for a guaranteed-failing DB lookup, which is strictly worse behavior.

Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-08T13:46:04.940Z
Learning: When reviewing Parse Server PRs that add new features, always check whether the feature is documented in the README.md file, though for new Parse Server options this is optional rather than required.

Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-17T15:02:48.786Z
Learning: For Parse Server PRs, always suggest an Angular commit convention PR title that would make a meaningful changelog entry for developers. Update the PR title suggestion on every commit. The format should be: type(scope): description. Common types include feat, fix, perf, refactor, docs, test, chore. The scope should identify the subsystem (e.g., graphql, rest, push, security). The description should be action-oriented and clearly convey the change's impact to developers.

Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2026-03-23T22:11:01.903Z
Learning: In Parse Server PRs (parse-community/parse-server), when the commit/PR type prefix is `fix:`, the PR title must describe the bug (the broken behavior), not the fix (the expected or corrected behavior). For example, instead of "fix: Strip undefined values in ParseServerRESTController to match HTTP mode" (describes the fix), prefer "fix: directAccess update with undefined field values persists them as null in MongoDB" (describes the broken behavior).

Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-17T15:02:24.824Z
Learning: For Parse Server PRs, always suggest an Angular-style PR title that would make a meaningful changelog entry for developers. Update the PR title suggestion with every new commit to the PR.

Learnt from: mtrezza
Repo: parse-community/parse-server PR: 10394
File: src/Routers/FilesRouter.js:94-111
Timestamp: 2026-04-03T18:23:49.632Z
Learning: In parse-community/parse-server `src/Routers/FilesRouter.js`, the old `_resolveAuth` helper that silently caught all `auth.getAuthForSessionToken` errors and fell back to anonymous/public access has been intentionally removed in PR `#10394`. File download auth now uses `initInfo` + `Middlewares.handleParseSession`, matching file upload behavior: invalid/stale session tokens on GET (download) routes are now rejected rather than silently falling back to public access. This alignment is a deliberate design decision — the silent fallback was considered the anomaly.

Learnt from: mtrezza
Repo: parse-community/parse-server PR: 10158
File: spec/CloudCode.spec.js:4473-4487
Timestamp: 2026-03-09T19:00:02.915Z
Learning: In parse-community/parse-server, the `responseHeaders` object exposed to `afterFind` file hooks (Parse.Cloud.afterFind(Parse.File, ...)) is intentionally fully mutable by the server administrator. Admins can add, modify, or delete headers — including default security headers like `X-Content-Type-Options: nosniff` — via `req.responseHeaders`. Removing a security header is treated as an explicit, conscious decision by the person who also holds the master key. Parse Server provides secure defaults but does not enforce them as non-overridable. This is consistent with the `forceDownload` pattern that also gives Cloud Code full control over file response behavior.

Learnt from: mtrezza
Repo: parse-community/parse-server PR: 10117
File: benchmark/performance.js:713-737
Timestamp: 2026-03-06T21:30:03.253Z
Learning: In Parse Server LiveQuery, use liveQuery: { classNames: [...] } in the server config to specify which classes emit pub/sub events (server-side producers). Use ParseServer.createLiveQueryServer(httpServer, { appId, masterKey, serverURL }) to start the WebSocket server that forwards events to subscribed clients (event consumers); this function does not require a classNames parameter, as it simply matches incoming events against active client subscriptions.

Learnt from: mtrezza
Repo: parse-community/parse-server PR: 10117
File: benchmark/performance.js:52-52
Timestamp: 2026-03-06T21:30:10.326Z
Learning: In reviews of JavaScript files, ensure separation of concerns for LiveQuery in Parse Server: do not rely on the LiveQuery server to perform className filtering. The filtering and class event emission should occur in the ParseServer configuration (classNames within liveQuery) or related server logic, while ParseServer.createLiveQueryServer(httpServer, { appId, masterKey, serverURL }) should only establish the WebSocket-based delivery of already-filtered events. Treat classNames filtering as the responsibility of ParseServer, not the LiveQuery server, and verify that LiveQuery code receives pre-filtered events and does not duplicate filtering logic.

Learnt from: mtrezza
Repo: parse-community/parse-server PR: 10225
File: spec/Utils.spec.js:399-419
Timestamp: 2026-03-16T21:02:42.989Z
Learning: In the parse-community/parse-server repository, Biome is not used. Do not flag Biome lint issues (e.g., lint/suspicious/noThenProperty) or suggest biome-ignore comments in any JavaScript files. Treat Biome-related checks as exempt for all JS sources under this repo; if CI enforces Biome, ensure this guidance is reflected in CI configuration accordingly.

Learnt from: mtrezza
Repo: parse-community/parse-server PR: 10389
File: src/middlewares.js:493-516
Timestamp: 2026-04-03T02:00:09.295Z
Learning: In this codebase (Parse Server using Express 5), do not require explicit try/catch + next(error) wrappers for async Express middleware. Express 5 automatically forwards errors from async middleware (including unhandled promise rejections) to the next error handler. Therefore, in src/middlewares.js, it is expected that async functions like handleParseHeaders and handleParseAuth can intentionally throw without try/catch as part of normal Express 5 behavior.

Copy link
Copy Markdown

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

🧹 Nitpick comments (1)
src/middlewares.js (1)

86-93: Add null guard to getHeaderAliases for defensive programming.

getHeaderAliases is exported and called from getCSRFRequestHeaders in ParseGraphQLServer.js with this.parseServer.config.headerAliases, which could theoretically be undefined before defaults are applied or if explicitly set to null. While src/defaults.js provides headerAliases: {} as default, adding a guard improves robustness.

♻️ Proposed fix
 export function getHeaderAliases(headerAliases, canonicalHeader) {
+  if (!headerAliases || typeof headerAliases !== 'object') {
+    return [];
+  }
   const aliases = headerAliases[canonicalHeader];
   if (!Array.isArray(aliases)) {
     return [];
   }
   // Clean up the aliases and remove any empty strings
   return aliases.map(alias => alias.trim()).filter(Boolean);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/middlewares.js` around lines 86 - 93, The getHeaderAliases function
should defensively handle a null/undefined headerAliases argument: check that
headerAliases is an object (and not null) before accessing
headerAliases[canonicalHeader], and return an empty array if headerAliases is
missing or not an object; then proceed to validate that the retrieved aliases is
an array and perform the existing trim/filter logic. Update getHeaderAliases to
early-return [] when headerAliases is falsy or not an object to avoid runtime
errors when called (e.g., from getCSRFRequestHeaders in ParseGraphQLServer.js)
and keep the rest of the function (aliases.map(...).filter(Boolean)) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/middlewares.js`:
- Around line 86-93: The getHeaderAliases function should defensively handle a
null/undefined headerAliases argument: check that headerAliases is an object
(and not null) before accessing headerAliases[canonicalHeader], and return an
empty array if headerAliases is missing or not an object; then proceed to
validate that the retrieved aliases is an array and perform the existing
trim/filter logic. Update getHeaderAliases to early-return [] when headerAliases
is falsy or not an object to avoid runtime errors when called (e.g., from
getCSRFRequestHeaders in ParseGraphQLServer.js) and keep the rest of the
function (aliases.map(...).filter(Boolean)) unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 23d169ab-15ff-4a69-afe5-7aed7b82a141

📥 Commits

Reviewing files that changed from the base of the PR and between a40d82c and 4a87fa9.

📒 Files selected for processing (6)
  • spec/HeaderAliasesValidation.spec.js
  • spec/Middlewares.spec.js
  • spec/ParseGraphQLServer.spec.js
  • src/Config.js
  • src/GraphQL/ParseGraphQLServer.js
  • src/middlewares.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/Config.js
  • spec/ParseGraphQLServer.spec.js

@mtrezza
Copy link
Copy Markdown
Member

mtrezza commented Apr 6, 2026

@Moumouls Is this ready for review? I see an open CR feedback...

@mtrezza
Copy link
Copy Markdown
Member

mtrezza commented Apr 6, 2026

Rebased...

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.

2 participants