Skip to content

fix(backend): strip private_metadata from resource _raw in SSR sanitizer - #8702

Merged
dominic-clerk merged 2 commits into
mainfrom
dc-strip-raw-private-metadata
Jun 2, 2026
Merged

fix(backend): strip private_metadata from resource _raw in SSR sanitizer#8702
dominic-clerk merged 2 commits into
mainfrom
dc-strip-raw-private-metadata

Conversation

@dominic-clerk

Copy link
Copy Markdown
Contributor

Description

stripPrivateDataFromObject now removes private_metadata from the backend resource _raw payload, preventing it from leaking into __clerk_ssr_state when a User/Organization resource is passed to buildClerkProps.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

stripPrivateDataFromObject now removes private_metadata from the backend
resource _raw payload, preventing it from leaking into __clerk_ssr_state
when a User/Organization resource is passed to buildClerkProps.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercelBot commented May 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentMay 29, 2026 4:34pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

Snapi: no API changes detected in @clerk/backend, @clerk/clerk-js, @clerk/nextjs, @clerk/react, @clerk/shared, @clerk/ui.

@pkg-pr-new

pkg-pr-newBot commented May 29, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@8702

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@8702

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@8702

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@8702

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@8702

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@8702

@clerk/express

npm i https://pkg.pr.new/@clerk/express@8702

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@8702

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@8702

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@8702

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@8702

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@8702

@clerk/react

npm i https://pkg.pr.new/@clerk/react@8702

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@8702

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@8702

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@8702

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@8702

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@8702

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@8702

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@8702

commit: cc7bcbc

@coderabbitai

coderabbitaiBot commented May 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 61e1e263-b0fd-4cd9-b22c-41a8e217599e

📥 Commits

Reviewing files that changed from the base of the PR and between 33198d5 and cc7bcbc.

📒 Files selected for processing (2)
  • packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts
  • packages/backend/src/util/decorateObjectWithResources.ts

📝 Walkthrough

Walkthrough

This PR expands private-data stripping to backend resource _raw payloads: the prune helper accepts optional _raw and uses redactPrivateMetadataDeep to recursively remove both private_metadata and privateMetadata keys from arrays and objects, replacing resource._raw with a sanitized clone. Tests confirm top-level and nested private keys are removed (including inside organization_memberships), public_metadata is preserved, and the original raw objects are not mutated. A changeset documents the fix to prevent leakage into __clerk_ssr_state during buildClerkProps.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly and specifically describes the main change: adding private_metadata stripping from resource _raw in the SSR sanitizer function.
Description check✅ PassedThe description is directly related to the changeset, explaining the bug fix and its purpose of preventing private_metadata leaks in __clerk_ssr_state.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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


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.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts (1)

10-11: ⚡ Quick win

Reduce as any usage in tests for safer fixtures.

These casts can be replaced with typed fixtures (e.g., Parameters<typeof User.fromJSON>[0] / Parameters<typeof Organization.fromJSON>[0]) to keep test intent while preserving type checks.

As per coding guidelines: "**/*.{ts,tsx}: Avoid any type - prefer unknown when type is uncertain, then narrow with type guards" and "No any types without justification in code review".

Also applies to: 29-29, 38-38, 47-48, 51-52, 65-65

🤖 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 `@packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts`
around lines 10 - 11, Replace the unsafe "as any" casts in the test fixtures
with properly typed fixture objects by using the parameter types of the model
deserializers (e.g., use Parameters<typeof User.fromJSON>[0] for the user
fixture and Parameters<typeof Organization.fromJSON>[0] for the organization
fixture) and adjust other occurrences similarly (lines referencing the same test
fixtures at the other locations) so the test inputs retain type checking; update
the test variable declarations to use those parameter types and pass the objects
through the existing fromJSON constructors (User.fromJSON,
Organization.fromJSON) or narrow with appropriate type guards instead of casting
to any.
🤖 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 `@packages/backend/src/util/decorateObjectWithResources.ts`:
- Line 55: The parameter types for prunePrivateMetadata should avoid any: change
the signature to use unknown for scalar-ish fields and Record<string, unknown>
for object-like fields (e.g., replace private_metadata?: any, privateMetadata?:
any, _raw?: any with private_metadata?: unknown | Record<string, unknown>,
privateMetadata?: unknown | Record<string, unknown>, _raw?: unknown |
Record<string, unknown>); update prunePrivateMetadata to use narrowings/type
guards (typeof checks and Object.prototype.toString or Array.isArray where
appropriate) or safe casts before accessing properties so the sanitizer remains
type-safe while preserving existing behavior for private_metadata,
privateMetadata, and _raw.
---
Nitpick comments:
In `@packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts`:
- Around line 10-11: Replace the unsafe "as any" casts in the test fixtures with
properly typed fixture objects by using the parameter types of the model
deserializers (e.g., use Parameters<typeof User.fromJSON>[0] for the user
fixture and Parameters<typeof Organization.fromJSON>[0] for the organization
fixture) and adjust other occurrences similarly (lines referencing the same test
fixtures at the other locations) so the test inputs retain type checking; update
the test variable declarations to use those parameter types and pass the objects
through the existing fromJSON constructors (User.fromJSON,
Organization.fromJSON) or narrow with appropriate type guards instead of casting
to any.
🪄 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: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: b1fc3d27-ae40-4507-881c-74abc9301b8e

📥 Commits

Reviewing files that changed from the base of the PR and between b9eb19d and 33198d5.

📒 Files selected for processing (3)
  • .changeset/proud-lions-allow.md
  • packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts
  • packages/backend/src/util/decorateObjectWithResources.ts

}

function prunePrivateMetadata(resource?: { private_metadata: any } | { privateMetadata: any } | null) {
function prunePrivateMetadata(resource?: { private_metadata?: any; privateMetadata?: any; _raw?: any } | null) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash# Verify current `any` usage in the touched sanitizer and related test file.
rg -nP --type=ts '\bany\b' packages/backend/src/util/decorateObjectWithResources.ts packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts

Repository: clerk/javascript

Length of output: 1441


🏁 Script executed:

#!/bin/bashset -euo pipefail
sed -n '40,100p' packages/backend/src/util/decorateObjectWithResources.ts | cat -n

Repository: clerk/javascript

Length of output: 1648


Replace any types in prunePrivateMetadata input

packages/backend/src/util/decorateObjectWithResources.ts uses any for private_metadata, privateMetadata, and _raw in the sanitizer signature (line 16); replace with unknown/Record<string, unknown> to keep the sanitization type-safe.

Suggested typed alternative
-function prunePrivateMetadata(resource?: { private_metadata?: any; privateMetadata?: any; _raw?: any } | null) {+type PrunableResource = {+ private_metadata?: unknown;+ privateMetadata?: unknown;+ _raw?: Record<string, unknown>;+} | null;++function prunePrivateMetadata(resource?: PrunableResource) {
🤖 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 `@packages/backend/src/util/decorateObjectWithResources.ts` at line 55, The
parameter types for prunePrivateMetadata should avoid any: change the signature
to use unknown for scalar-ish fields and Record<string, unknown> for object-like
fields (e.g., replace private_metadata?: any, privateMetadata?: any, _raw?: any
with private_metadata?: unknown | Record<string, unknown>, privateMetadata?:
unknown | Record<string, unknown>, _raw?: unknown | Record<string, unknown>);
update prunePrivateMetadata to use narrowings/type guards (typeof checks and
Object.prototype.toString or Array.isArray where appropriate) or safe casts
before accessing properties so the sanitizer remains type-safe while preserving
existing behavior for private_metadata, privateMetadata, and _raw.

@jacekradko

Copy link
Copy Markdown
Contributor

AI finding (P1): stripPrivateDataFromObject currently removes top-level _raw.private_metadata, but UserJSON may include nested organization_memberships, and each membership can include its own private_metadata plus a nested organization.private_metadata. Those nested fields would still serialize into __clerk_ssr_state if present, so the SSR leak is only partially fixed.

Please recursively redact private_metadata / privateMetadata from the cloned _raw payload, or at least cover organization_memberships[*] and nested organization, and add a regression test for that shape.

Relevant spots:

  • packages/backend/src/util/decorateObjectWithResources.ts:70
  • packages/backend/src/api/resources/JSON.ts:683
  • packages/backend/src/api/resources/JSON.ts:498

…n SSR sanitizer
The previous fix only removed the top-level `_raw.private_metadata`, but a
`User`'s `_raw` payload nests further private metadata — each
`organization_memberships[*]` carries its own `private_metadata` plus a nested
`organization.private_metadata` — which still serialized into
`__clerk_ssr_state`.
Redact `private_metadata`/`privateMetadata` recursively on a deep clone of
`_raw` so nested fields are stripped while the original resource (and its `raw`
getter) is left untouched. Adds a regression test for the
`organization_memberships` shape.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: cc7bcbc

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
NameType
@clerk/backendPatch
@clerk/astroPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/honoPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/tanstack-react-startPatch
@clerk/testingPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@dominic-clerk

Copy link
Copy Markdown
ContributorAuthor

New commit addresses the finding

@jacekradkojacekradko left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

:shipit:

@dominic-clerk
dominic-clerk merged commit fb184de into mainJun 2, 2026
45 checks passed
@dominic-clerk
dominic-clerk deleted the dc-strip-raw-private-metadata branch June 2, 2026 15:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@dominic-clerk@jacekradko
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
fix(backend): strip private_metadata from resource _raw in SSR sanitizer by dominic-clerk · Pull Request #8702 · clerk/javascript · GitHub
Skip to content

fix(backend): strip private_metadata from resource _raw in SSR sanitizer - #8702

Merged
dominic-clerk merged 2 commits into
mainfrom
dc-strip-raw-private-metadata
Jun 2, 2026
Merged

fix(backend): strip private_metadata from resource _raw in SSR sanitizer#8702
dominic-clerk merged 2 commits into
mainfrom
dc-strip-raw-private-metadata

Conversation

@dominic-clerk

Copy link
Copy Markdown
Contributor

Description

stripPrivateDataFromObject now removes private_metadata from the backend resource _raw payload, preventing it from leaking into __clerk_ssr_state when a User/Organization resource is passed to buildClerkProps.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

stripPrivateDataFromObject now removes private_metadata from the backend
resource _raw payload, preventing it from leaking into __clerk_ssr_state
when a User/Organization resource is passed to buildClerkProps.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercelBot commented May 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentMay 29, 2026 4:34pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

Snapi: no API changes detected in @clerk/backend, @clerk/clerk-js, @clerk/nextjs, @clerk/react, @clerk/shared, @clerk/ui.

@pkg-pr-new

pkg-pr-newBot commented May 29, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@8702

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@8702

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@8702

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@8702

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@8702

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@8702

@clerk/express

npm i https://pkg.pr.new/@clerk/express@8702

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@8702

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@8702

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@8702

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@8702

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@8702

@clerk/react

npm i https://pkg.pr.new/@clerk/react@8702

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@8702

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@8702

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@8702

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@8702

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@8702

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@8702

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@8702

commit: cc7bcbc

@coderabbitai

coderabbitaiBot commented May 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 61e1e263-b0fd-4cd9-b22c-41a8e217599e

📥 Commits

Reviewing files that changed from the base of the PR and between 33198d5 and cc7bcbc.

📒 Files selected for processing (2)
  • packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts
  • packages/backend/src/util/decorateObjectWithResources.ts

📝 Walkthrough

Walkthrough

This PR expands private-data stripping to backend resource _raw payloads: the prune helper accepts optional _raw and uses redactPrivateMetadataDeep to recursively remove both private_metadata and privateMetadata keys from arrays and objects, replacing resource._raw with a sanitized clone. Tests confirm top-level and nested private keys are removed (including inside organization_memberships), public_metadata is preserved, and the original raw objects are not mutated. A changeset documents the fix to prevent leakage into __clerk_ssr_state during buildClerkProps.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly and specifically describes the main change: adding private_metadata stripping from resource _raw in the SSR sanitizer function.
Description check✅ PassedThe description is directly related to the changeset, explaining the bug fix and its purpose of preventing private_metadata leaks in __clerk_ssr_state.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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


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.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts (1)

10-11: ⚡ Quick win

Reduce as any usage in tests for safer fixtures.

These casts can be replaced with typed fixtures (e.g., Parameters<typeof User.fromJSON>[0] / Parameters<typeof Organization.fromJSON>[0]) to keep test intent while preserving type checks.

As per coding guidelines: "**/*.{ts,tsx}: Avoid any type - prefer unknown when type is uncertain, then narrow with type guards" and "No any types without justification in code review".

Also applies to: 29-29, 38-38, 47-48, 51-52, 65-65

🤖 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 `@packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts`
around lines 10 - 11, Replace the unsafe "as any" casts in the test fixtures
with properly typed fixture objects by using the parameter types of the model
deserializers (e.g., use Parameters<typeof User.fromJSON>[0] for the user
fixture and Parameters<typeof Organization.fromJSON>[0] for the organization
fixture) and adjust other occurrences similarly (lines referencing the same test
fixtures at the other locations) so the test inputs retain type checking; update
the test variable declarations to use those parameter types and pass the objects
through the existing fromJSON constructors (User.fromJSON,
Organization.fromJSON) or narrow with appropriate type guards instead of casting
to any.
🤖 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 `@packages/backend/src/util/decorateObjectWithResources.ts`:
- Line 55: The parameter types for prunePrivateMetadata should avoid any: change
the signature to use unknown for scalar-ish fields and Record<string, unknown>
for object-like fields (e.g., replace private_metadata?: any, privateMetadata?:
any, _raw?: any with private_metadata?: unknown | Record<string, unknown>,
privateMetadata?: unknown | Record<string, unknown>, _raw?: unknown |
Record<string, unknown>); update prunePrivateMetadata to use narrowings/type
guards (typeof checks and Object.prototype.toString or Array.isArray where
appropriate) or safe casts before accessing properties so the sanitizer remains
type-safe while preserving existing behavior for private_metadata,
privateMetadata, and _raw.
---
Nitpick comments:
In `@packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts`:
- Around line 10-11: Replace the unsafe "as any" casts in the test fixtures with
properly typed fixture objects by using the parameter types of the model
deserializers (e.g., use Parameters<typeof User.fromJSON>[0] for the user
fixture and Parameters<typeof Organization.fromJSON>[0] for the organization
fixture) and adjust other occurrences similarly (lines referencing the same test
fixtures at the other locations) so the test inputs retain type checking; update
the test variable declarations to use those parameter types and pass the objects
through the existing fromJSON constructors (User.fromJSON,
Organization.fromJSON) or narrow with appropriate type guards instead of casting
to any.
🪄 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: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: b1fc3d27-ae40-4507-881c-74abc9301b8e

📥 Commits

Reviewing files that changed from the base of the PR and between b9eb19d and 33198d5.

📒 Files selected for processing (3)
  • .changeset/proud-lions-allow.md
  • packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts
  • packages/backend/src/util/decorateObjectWithResources.ts

}

function prunePrivateMetadata(resource?: { private_metadata: any } | { privateMetadata: any } | null) {
function prunePrivateMetadata(resource?: { private_metadata?: any; privateMetadata?: any; _raw?: any } | null) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash# Verify current `any` usage in the touched sanitizer and related test file.
rg -nP --type=ts '\bany\b' packages/backend/src/util/decorateObjectWithResources.ts packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts

Repository: clerk/javascript

Length of output: 1441


🏁 Script executed:

#!/bin/bashset -euo pipefail
sed -n '40,100p' packages/backend/src/util/decorateObjectWithResources.ts | cat -n

Repository: clerk/javascript

Length of output: 1648


Replace any types in prunePrivateMetadata input

packages/backend/src/util/decorateObjectWithResources.ts uses any for private_metadata, privateMetadata, and _raw in the sanitizer signature (line 16); replace with unknown/Record<string, unknown> to keep the sanitization type-safe.

Suggested typed alternative
-function prunePrivateMetadata(resource?: { private_metadata?: any; privateMetadata?: any; _raw?: any } | null) {+type PrunableResource = {+ private_metadata?: unknown;+ privateMetadata?: unknown;+ _raw?: Record<string, unknown>;+} | null;++function prunePrivateMetadata(resource?: PrunableResource) {
🤖 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 `@packages/backend/src/util/decorateObjectWithResources.ts` at line 55, The
parameter types for prunePrivateMetadata should avoid any: change the signature
to use unknown for scalar-ish fields and Record<string, unknown> for object-like
fields (e.g., replace private_metadata?: any, privateMetadata?: any, _raw?: any
with private_metadata?: unknown | Record<string, unknown>, privateMetadata?:
unknown | Record<string, unknown>, _raw?: unknown | Record<string, unknown>);
update prunePrivateMetadata to use narrowings/type guards (typeof checks and
Object.prototype.toString or Array.isArray where appropriate) or safe casts
before accessing properties so the sanitizer remains type-safe while preserving
existing behavior for private_metadata, privateMetadata, and _raw.

@jacekradko

Copy link
Copy Markdown
Contributor

AI finding (P1): stripPrivateDataFromObject currently removes top-level _raw.private_metadata, but UserJSON may include nested organization_memberships, and each membership can include its own private_metadata plus a nested organization.private_metadata. Those nested fields would still serialize into __clerk_ssr_state if present, so the SSR leak is only partially fixed.

Please recursively redact private_metadata / privateMetadata from the cloned _raw payload, or at least cover organization_memberships[*] and nested organization, and add a regression test for that shape.

Relevant spots:

  • packages/backend/src/util/decorateObjectWithResources.ts:70
  • packages/backend/src/api/resources/JSON.ts:683
  • packages/backend/src/api/resources/JSON.ts:498

…n SSR sanitizer
The previous fix only removed the top-level `_raw.private_metadata`, but a
`User`'s `_raw` payload nests further private metadata — each
`organization_memberships[*]` carries its own `private_metadata` plus a nested
`organization.private_metadata` — which still serialized into
`__clerk_ssr_state`.
Redact `private_metadata`/`privateMetadata` recursively on a deep clone of
`_raw` so nested fields are stripped while the original resource (and its `raw`
getter) is left untouched. Adds a regression test for the
`organization_memberships` shape.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: cc7bcbc

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
NameType
@clerk/backendPatch
@clerk/astroPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/honoPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/tanstack-react-startPatch
@clerk/testingPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@dominic-clerk

Copy link
Copy Markdown
ContributorAuthor

New commit addresses the finding

@jacekradkojacekradko left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

:shipit:

@dominic-clerk
dominic-clerk merged commit fb184de into mainJun 2, 2026
45 checks passed
@dominic-clerk
dominic-clerk deleted the dc-strip-raw-private-metadata branch June 2, 2026 15:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@dominic-clerk@jacekradko
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(backend): strip private_metadata from resource _raw in SSR sanitizer by dominic-clerk · Pull Request #8702 · clerk/javascript · GitHub
Skip to content

fix(backend): strip private_metadata from resource _raw in SSR sanitizer - #8702

Merged
dominic-clerk merged 2 commits into
mainfrom
dc-strip-raw-private-metadata
Jun 2, 2026
Merged

fix(backend): strip private_metadata from resource _raw in SSR sanitizer#8702
dominic-clerk merged 2 commits into
mainfrom
dc-strip-raw-private-metadata

Conversation

@dominic-clerk

Copy link
Copy Markdown
Contributor

Description

stripPrivateDataFromObject now removes private_metadata from the backend resource _raw payload, preventing it from leaking into __clerk_ssr_state when a User/Organization resource is passed to buildClerkProps.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

stripPrivateDataFromObject now removes private_metadata from the backend
resource _raw payload, preventing it from leaking into __clerk_ssr_state
when a User/Organization resource is passed to buildClerkProps.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercelBot commented May 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentMay 29, 2026 4:34pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

Snapi: no API changes detected in @clerk/backend, @clerk/clerk-js, @clerk/nextjs, @clerk/react, @clerk/shared, @clerk/ui.

@pkg-pr-new

pkg-pr-newBot commented May 29, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@8702

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@8702

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@8702

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@8702

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@8702

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@8702

@clerk/express

npm i https://pkg.pr.new/@clerk/express@8702

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@8702

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@8702

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@8702

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@8702

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@8702

@clerk/react

npm i https://pkg.pr.new/@clerk/react@8702

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@8702

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@8702

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@8702

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@8702

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@8702

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@8702

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@8702

commit: cc7bcbc

@coderabbitai

coderabbitaiBot commented May 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 61e1e263-b0fd-4cd9-b22c-41a8e217599e

📥 Commits

Reviewing files that changed from the base of the PR and between 33198d5 and cc7bcbc.

📒 Files selected for processing (2)
  • packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts
  • packages/backend/src/util/decorateObjectWithResources.ts

📝 Walkthrough

Walkthrough

This PR expands private-data stripping to backend resource _raw payloads: the prune helper accepts optional _raw and uses redactPrivateMetadataDeep to recursively remove both private_metadata and privateMetadata keys from arrays and objects, replacing resource._raw with a sanitized clone. Tests confirm top-level and nested private keys are removed (including inside organization_memberships), public_metadata is preserved, and the original raw objects are not mutated. A changeset documents the fix to prevent leakage into __clerk_ssr_state during buildClerkProps.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly and specifically describes the main change: adding private_metadata stripping from resource _raw in the SSR sanitizer function.
Description check✅ PassedThe description is directly related to the changeset, explaining the bug fix and its purpose of preventing private_metadata leaks in __clerk_ssr_state.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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


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.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts (1)

10-11: ⚡ Quick win

Reduce as any usage in tests for safer fixtures.

These casts can be replaced with typed fixtures (e.g., Parameters<typeof User.fromJSON>[0] / Parameters<typeof Organization.fromJSON>[0]) to keep test intent while preserving type checks.

As per coding guidelines: "**/*.{ts,tsx}: Avoid any type - prefer unknown when type is uncertain, then narrow with type guards" and "No any types without justification in code review".

Also applies to: 29-29, 38-38, 47-48, 51-52, 65-65

🤖 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 `@packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts`
around lines 10 - 11, Replace the unsafe "as any" casts in the test fixtures
with properly typed fixture objects by using the parameter types of the model
deserializers (e.g., use Parameters<typeof User.fromJSON>[0] for the user
fixture and Parameters<typeof Organization.fromJSON>[0] for the organization
fixture) and adjust other occurrences similarly (lines referencing the same test
fixtures at the other locations) so the test inputs retain type checking; update
the test variable declarations to use those parameter types and pass the objects
through the existing fromJSON constructors (User.fromJSON,
Organization.fromJSON) or narrow with appropriate type guards instead of casting
to any.
🤖 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 `@packages/backend/src/util/decorateObjectWithResources.ts`:
- Line 55: The parameter types for prunePrivateMetadata should avoid any: change
the signature to use unknown for scalar-ish fields and Record<string, unknown>
for object-like fields (e.g., replace private_metadata?: any, privateMetadata?:
any, _raw?: any with private_metadata?: unknown | Record<string, unknown>,
privateMetadata?: unknown | Record<string, unknown>, _raw?: unknown |
Record<string, unknown>); update prunePrivateMetadata to use narrowings/type
guards (typeof checks and Object.prototype.toString or Array.isArray where
appropriate) or safe casts before accessing properties so the sanitizer remains
type-safe while preserving existing behavior for private_metadata,
privateMetadata, and _raw.
---
Nitpick comments:
In `@packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts`:
- Around line 10-11: Replace the unsafe "as any" casts in the test fixtures with
properly typed fixture objects by using the parameter types of the model
deserializers (e.g., use Parameters<typeof User.fromJSON>[0] for the user
fixture and Parameters<typeof Organization.fromJSON>[0] for the organization
fixture) and adjust other occurrences similarly (lines referencing the same test
fixtures at the other locations) so the test inputs retain type checking; update
the test variable declarations to use those parameter types and pass the objects
through the existing fromJSON constructors (User.fromJSON,
Organization.fromJSON) or narrow with appropriate type guards instead of casting
to any.
🪄 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: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: b1fc3d27-ae40-4507-881c-74abc9301b8e

📥 Commits

Reviewing files that changed from the base of the PR and between b9eb19d and 33198d5.

📒 Files selected for processing (3)
  • .changeset/proud-lions-allow.md
  • packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts
  • packages/backend/src/util/decorateObjectWithResources.ts

}

function prunePrivateMetadata(resource?: { private_metadata: any } | { privateMetadata: any } | null) {
function prunePrivateMetadata(resource?: { private_metadata?: any; privateMetadata?: any; _raw?: any } | null) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash# Verify current `any` usage in the touched sanitizer and related test file.
rg -nP --type=ts '\bany\b' packages/backend/src/util/decorateObjectWithResources.ts packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts

Repository: clerk/javascript

Length of output: 1441


🏁 Script executed:

#!/bin/bashset -euo pipefail
sed -n '40,100p' packages/backend/src/util/decorateObjectWithResources.ts | cat -n

Repository: clerk/javascript

Length of output: 1648


Replace any types in prunePrivateMetadata input

packages/backend/src/util/decorateObjectWithResources.ts uses any for private_metadata, privateMetadata, and _raw in the sanitizer signature (line 16); replace with unknown/Record<string, unknown> to keep the sanitization type-safe.

Suggested typed alternative
-function prunePrivateMetadata(resource?: { private_metadata?: any; privateMetadata?: any; _raw?: any } | null) {+type PrunableResource = {+ private_metadata?: unknown;+ privateMetadata?: unknown;+ _raw?: Record<string, unknown>;+} | null;++function prunePrivateMetadata(resource?: PrunableResource) {
🤖 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 `@packages/backend/src/util/decorateObjectWithResources.ts` at line 55, The
parameter types for prunePrivateMetadata should avoid any: change the signature
to use unknown for scalar-ish fields and Record<string, unknown> for object-like
fields (e.g., replace private_metadata?: any, privateMetadata?: any, _raw?: any
with private_metadata?: unknown | Record<string, unknown>, privateMetadata?:
unknown | Record<string, unknown>, _raw?: unknown | Record<string, unknown>);
update prunePrivateMetadata to use narrowings/type guards (typeof checks and
Object.prototype.toString or Array.isArray where appropriate) or safe casts
before accessing properties so the sanitizer remains type-safe while preserving
existing behavior for private_metadata, privateMetadata, and _raw.

@jacekradko

Copy link
Copy Markdown
Contributor

AI finding (P1): stripPrivateDataFromObject currently removes top-level _raw.private_metadata, but UserJSON may include nested organization_memberships, and each membership can include its own private_metadata plus a nested organization.private_metadata. Those nested fields would still serialize into __clerk_ssr_state if present, so the SSR leak is only partially fixed.

Please recursively redact private_metadata / privateMetadata from the cloned _raw payload, or at least cover organization_memberships[*] and nested organization, and add a regression test for that shape.

Relevant spots:

  • packages/backend/src/util/decorateObjectWithResources.ts:70
  • packages/backend/src/api/resources/JSON.ts:683
  • packages/backend/src/api/resources/JSON.ts:498

…n SSR sanitizer
The previous fix only removed the top-level `_raw.private_metadata`, but a
`User`'s `_raw` payload nests further private metadata — each
`organization_memberships[*]` carries its own `private_metadata` plus a nested
`organization.private_metadata` — which still serialized into
`__clerk_ssr_state`.
Redact `private_metadata`/`privateMetadata` recursively on a deep clone of
`_raw` so nested fields are stripped while the original resource (and its `raw`
getter) is left untouched. Adds a regression test for the
`organization_memberships` shape.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: cc7bcbc

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
NameType
@clerk/backendPatch
@clerk/astroPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/honoPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/tanstack-react-startPatch
@clerk/testingPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@dominic-clerk

Copy link
Copy Markdown
ContributorAuthor

New commit addresses the finding

@jacekradkojacekradko left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

:shipit:

@dominic-clerk
dominic-clerk merged commit fb184de into mainJun 2, 2026
45 checks passed
@dominic-clerk
dominic-clerk deleted the dc-strip-raw-private-metadata branch June 2, 2026 15:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@dominic-clerk@jacekradko
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(backend): strip private_metadata from resource _raw in SSR sanitizer by dominic-clerk · Pull Request #8702 · clerk/javascript · GitHub
Skip to content

fix(backend): strip private_metadata from resource _raw in SSR sanitizer - #8702

Merged
dominic-clerk merged 2 commits into
mainfrom
dc-strip-raw-private-metadata
Jun 2, 2026
Merged

fix(backend): strip private_metadata from resource _raw in SSR sanitizer#8702
dominic-clerk merged 2 commits into
mainfrom
dc-strip-raw-private-metadata

Conversation

@dominic-clerk

Copy link
Copy Markdown
Contributor

Description

stripPrivateDataFromObject now removes private_metadata from the backend resource _raw payload, preventing it from leaking into __clerk_ssr_state when a User/Organization resource is passed to buildClerkProps.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

stripPrivateDataFromObject now removes private_metadata from the backend
resource _raw payload, preventing it from leaking into __clerk_ssr_state
when a User/Organization resource is passed to buildClerkProps.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercelBot commented May 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentMay 29, 2026 4:34pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

Snapi: no API changes detected in @clerk/backend, @clerk/clerk-js, @clerk/nextjs, @clerk/react, @clerk/shared, @clerk/ui.

@pkg-pr-new

pkg-pr-newBot commented May 29, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@8702

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@8702

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@8702

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@8702

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@8702

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@8702

@clerk/express

npm i https://pkg.pr.new/@clerk/express@8702

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@8702

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@8702

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@8702

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@8702

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@8702

@clerk/react

npm i https://pkg.pr.new/@clerk/react@8702

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@8702

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@8702

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@8702

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@8702

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@8702

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@8702

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@8702

commit: cc7bcbc

@coderabbitai

coderabbitaiBot commented May 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 61e1e263-b0fd-4cd9-b22c-41a8e217599e

📥 Commits

Reviewing files that changed from the base of the PR and between 33198d5 and cc7bcbc.

📒 Files selected for processing (2)
  • packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts
  • packages/backend/src/util/decorateObjectWithResources.ts

📝 Walkthrough

Walkthrough

This PR expands private-data stripping to backend resource _raw payloads: the prune helper accepts optional _raw and uses redactPrivateMetadataDeep to recursively remove both private_metadata and privateMetadata keys from arrays and objects, replacing resource._raw with a sanitized clone. Tests confirm top-level and nested private keys are removed (including inside organization_memberships), public_metadata is preserved, and the original raw objects are not mutated. A changeset documents the fix to prevent leakage into __clerk_ssr_state during buildClerkProps.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly and specifically describes the main change: adding private_metadata stripping from resource _raw in the SSR sanitizer function.
Description check✅ PassedThe description is directly related to the changeset, explaining the bug fix and its purpose of preventing private_metadata leaks in __clerk_ssr_state.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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


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.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts (1)

10-11: ⚡ Quick win

Reduce as any usage in tests for safer fixtures.

These casts can be replaced with typed fixtures (e.g., Parameters<typeof User.fromJSON>[0] / Parameters<typeof Organization.fromJSON>[0]) to keep test intent while preserving type checks.

As per coding guidelines: "**/*.{ts,tsx}: Avoid any type - prefer unknown when type is uncertain, then narrow with type guards" and "No any types without justification in code review".

Also applies to: 29-29, 38-38, 47-48, 51-52, 65-65

🤖 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 `@packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts`
around lines 10 - 11, Replace the unsafe "as any" casts in the test fixtures
with properly typed fixture objects by using the parameter types of the model
deserializers (e.g., use Parameters<typeof User.fromJSON>[0] for the user
fixture and Parameters<typeof Organization.fromJSON>[0] for the organization
fixture) and adjust other occurrences similarly (lines referencing the same test
fixtures at the other locations) so the test inputs retain type checking; update
the test variable declarations to use those parameter types and pass the objects
through the existing fromJSON constructors (User.fromJSON,
Organization.fromJSON) or narrow with appropriate type guards instead of casting
to any.
🤖 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 `@packages/backend/src/util/decorateObjectWithResources.ts`:
- Line 55: The parameter types for prunePrivateMetadata should avoid any: change
the signature to use unknown for scalar-ish fields and Record<string, unknown>
for object-like fields (e.g., replace private_metadata?: any, privateMetadata?:
any, _raw?: any with private_metadata?: unknown | Record<string, unknown>,
privateMetadata?: unknown | Record<string, unknown>, _raw?: unknown |
Record<string, unknown>); update prunePrivateMetadata to use narrowings/type
guards (typeof checks and Object.prototype.toString or Array.isArray where
appropriate) or safe casts before accessing properties so the sanitizer remains
type-safe while preserving existing behavior for private_metadata,
privateMetadata, and _raw.
---
Nitpick comments:
In `@packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts`:
- Around line 10-11: Replace the unsafe "as any" casts in the test fixtures with
properly typed fixture objects by using the parameter types of the model
deserializers (e.g., use Parameters<typeof User.fromJSON>[0] for the user
fixture and Parameters<typeof Organization.fromJSON>[0] for the organization
fixture) and adjust other occurrences similarly (lines referencing the same test
fixtures at the other locations) so the test inputs retain type checking; update
the test variable declarations to use those parameter types and pass the objects
through the existing fromJSON constructors (User.fromJSON,
Organization.fromJSON) or narrow with appropriate type guards instead of casting
to any.
🪄 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: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: b1fc3d27-ae40-4507-881c-74abc9301b8e

📥 Commits

Reviewing files that changed from the base of the PR and between b9eb19d and 33198d5.

📒 Files selected for processing (3)
  • .changeset/proud-lions-allow.md
  • packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts
  • packages/backend/src/util/decorateObjectWithResources.ts

}

function prunePrivateMetadata(resource?: { private_metadata: any } | { privateMetadata: any } | null) {
function prunePrivateMetadata(resource?: { private_metadata?: any; privateMetadata?: any; _raw?: any } | null) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash# Verify current `any` usage in the touched sanitizer and related test file.
rg -nP --type=ts '\bany\b' packages/backend/src/util/decorateObjectWithResources.ts packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts

Repository: clerk/javascript

Length of output: 1441


🏁 Script executed:

#!/bin/bashset -euo pipefail
sed -n '40,100p' packages/backend/src/util/decorateObjectWithResources.ts | cat -n

Repository: clerk/javascript

Length of output: 1648


Replace any types in prunePrivateMetadata input

packages/backend/src/util/decorateObjectWithResources.ts uses any for private_metadata, privateMetadata, and _raw in the sanitizer signature (line 16); replace with unknown/Record<string, unknown> to keep the sanitization type-safe.

Suggested typed alternative
-function prunePrivateMetadata(resource?: { private_metadata?: any; privateMetadata?: any; _raw?: any } | null) {+type PrunableResource = {+ private_metadata?: unknown;+ privateMetadata?: unknown;+ _raw?: Record<string, unknown>;+} | null;++function prunePrivateMetadata(resource?: PrunableResource) {
🤖 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 `@packages/backend/src/util/decorateObjectWithResources.ts` at line 55, The
parameter types for prunePrivateMetadata should avoid any: change the signature
to use unknown for scalar-ish fields and Record<string, unknown> for object-like
fields (e.g., replace private_metadata?: any, privateMetadata?: any, _raw?: any
with private_metadata?: unknown | Record<string, unknown>, privateMetadata?:
unknown | Record<string, unknown>, _raw?: unknown | Record<string, unknown>);
update prunePrivateMetadata to use narrowings/type guards (typeof checks and
Object.prototype.toString or Array.isArray where appropriate) or safe casts
before accessing properties so the sanitizer remains type-safe while preserving
existing behavior for private_metadata, privateMetadata, and _raw.

@jacekradko

Copy link
Copy Markdown
Contributor

AI finding (P1): stripPrivateDataFromObject currently removes top-level _raw.private_metadata, but UserJSON may include nested organization_memberships, and each membership can include its own private_metadata plus a nested organization.private_metadata. Those nested fields would still serialize into __clerk_ssr_state if present, so the SSR leak is only partially fixed.

Please recursively redact private_metadata / privateMetadata from the cloned _raw payload, or at least cover organization_memberships[*] and nested organization, and add a regression test for that shape.

Relevant spots:

  • packages/backend/src/util/decorateObjectWithResources.ts:70
  • packages/backend/src/api/resources/JSON.ts:683
  • packages/backend/src/api/resources/JSON.ts:498

…n SSR sanitizer
The previous fix only removed the top-level `_raw.private_metadata`, but a
`User`'s `_raw` payload nests further private metadata — each
`organization_memberships[*]` carries its own `private_metadata` plus a nested
`organization.private_metadata` — which still serialized into
`__clerk_ssr_state`.
Redact `private_metadata`/`privateMetadata` recursively on a deep clone of
`_raw` so nested fields are stripped while the original resource (and its `raw`
getter) is left untouched. Adds a regression test for the
`organization_memberships` shape.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: cc7bcbc

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
NameType
@clerk/backendPatch
@clerk/astroPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/honoPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/tanstack-react-startPatch
@clerk/testingPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@dominic-clerk

Copy link
Copy Markdown
ContributorAuthor

New commit addresses the finding

@jacekradkojacekradko left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

:shipit:

@dominic-clerk
dominic-clerk merged commit fb184de into mainJun 2, 2026
45 checks passed
@dominic-clerk
dominic-clerk deleted the dc-strip-raw-private-metadata branch June 2, 2026 15:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@dominic-clerk@jacekradko
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' fix(backend): strip private_metadata from resource _raw in SSR sanitizer by dominic-clerk · Pull Request #8702 · clerk/javascript · GitHub
Skip to content

fix(backend): strip private_metadata from resource _raw in SSR sanitizer - #8702

Merged
dominic-clerk merged 2 commits into
mainfrom
dc-strip-raw-private-metadata
Jun 2, 2026
Merged

fix(backend): strip private_metadata from resource _raw in SSR sanitizer#8702
dominic-clerk merged 2 commits into
mainfrom
dc-strip-raw-private-metadata

Conversation

@dominic-clerk

Copy link
Copy Markdown
Contributor

Description

stripPrivateDataFromObject now removes private_metadata from the backend resource _raw payload, preventing it from leaking into __clerk_ssr_state when a User/Organization resource is passed to buildClerkProps.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

stripPrivateDataFromObject now removes private_metadata from the backend
resource _raw payload, preventing it from leaking into __clerk_ssr_state
when a User/Organization resource is passed to buildClerkProps.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercelBot commented May 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentMay 29, 2026 4:34pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

Snapi: no API changes detected in @clerk/backend, @clerk/clerk-js, @clerk/nextjs, @clerk/react, @clerk/shared, @clerk/ui.

@pkg-pr-new

pkg-pr-newBot commented May 29, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@8702

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@8702

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@8702

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@8702

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@8702

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@8702

@clerk/express

npm i https://pkg.pr.new/@clerk/express@8702

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@8702

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@8702

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@8702

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@8702

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@8702

@clerk/react

npm i https://pkg.pr.new/@clerk/react@8702

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@8702

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@8702

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@8702

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@8702

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@8702

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@8702

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@8702

commit: cc7bcbc

@coderabbitai

coderabbitaiBot commented May 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 61e1e263-b0fd-4cd9-b22c-41a8e217599e

📥 Commits

Reviewing files that changed from the base of the PR and between 33198d5 and cc7bcbc.

📒 Files selected for processing (2)
  • packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts
  • packages/backend/src/util/decorateObjectWithResources.ts

📝 Walkthrough

Walkthrough

This PR expands private-data stripping to backend resource _raw payloads: the prune helper accepts optional _raw and uses redactPrivateMetadataDeep to recursively remove both private_metadata and privateMetadata keys from arrays and objects, replacing resource._raw with a sanitized clone. Tests confirm top-level and nested private keys are removed (including inside organization_memberships), public_metadata is preserved, and the original raw objects are not mutated. A changeset documents the fix to prevent leakage into __clerk_ssr_state during buildClerkProps.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly and specifically describes the main change: adding private_metadata stripping from resource _raw in the SSR sanitizer function.
Description check✅ PassedThe description is directly related to the changeset, explaining the bug fix and its purpose of preventing private_metadata leaks in __clerk_ssr_state.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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


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.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts (1)

10-11: ⚡ Quick win

Reduce as any usage in tests for safer fixtures.

These casts can be replaced with typed fixtures (e.g., Parameters<typeof User.fromJSON>[0] / Parameters<typeof Organization.fromJSON>[0]) to keep test intent while preserving type checks.

As per coding guidelines: "**/*.{ts,tsx}: Avoid any type - prefer unknown when type is uncertain, then narrow with type guards" and "No any types without justification in code review".

Also applies to: 29-29, 38-38, 47-48, 51-52, 65-65

🤖 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 `@packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts`
around lines 10 - 11, Replace the unsafe "as any" casts in the test fixtures
with properly typed fixture objects by using the parameter types of the model
deserializers (e.g., use Parameters<typeof User.fromJSON>[0] for the user
fixture and Parameters<typeof Organization.fromJSON>[0] for the organization
fixture) and adjust other occurrences similarly (lines referencing the same test
fixtures at the other locations) so the test inputs retain type checking; update
the test variable declarations to use those parameter types and pass the objects
through the existing fromJSON constructors (User.fromJSON,
Organization.fromJSON) or narrow with appropriate type guards instead of casting
to any.
🤖 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 `@packages/backend/src/util/decorateObjectWithResources.ts`:
- Line 55: The parameter types for prunePrivateMetadata should avoid any: change
the signature to use unknown for scalar-ish fields and Record<string, unknown>
for object-like fields (e.g., replace private_metadata?: any, privateMetadata?:
any, _raw?: any with private_metadata?: unknown | Record<string, unknown>,
privateMetadata?: unknown | Record<string, unknown>, _raw?: unknown |
Record<string, unknown>); update prunePrivateMetadata to use narrowings/type
guards (typeof checks and Object.prototype.toString or Array.isArray where
appropriate) or safe casts before accessing properties so the sanitizer remains
type-safe while preserving existing behavior for private_metadata,
privateMetadata, and _raw.
---
Nitpick comments:
In `@packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts`:
- Around line 10-11: Replace the unsafe "as any" casts in the test fixtures with
properly typed fixture objects by using the parameter types of the model
deserializers (e.g., use Parameters<typeof User.fromJSON>[0] for the user
fixture and Parameters<typeof Organization.fromJSON>[0] for the organization
fixture) and adjust other occurrences similarly (lines referencing the same test
fixtures at the other locations) so the test inputs retain type checking; update
the test variable declarations to use those parameter types and pass the objects
through the existing fromJSON constructors (User.fromJSON,
Organization.fromJSON) or narrow with appropriate type guards instead of casting
to any.
🪄 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: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: b1fc3d27-ae40-4507-881c-74abc9301b8e

📥 Commits

Reviewing files that changed from the base of the PR and between b9eb19d and 33198d5.

📒 Files selected for processing (3)
  • .changeset/proud-lions-allow.md
  • packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts
  • packages/backend/src/util/decorateObjectWithResources.ts

}

function prunePrivateMetadata(resource?: { private_metadata: any } | { privateMetadata: any } | null) {
function prunePrivateMetadata(resource?: { private_metadata?: any; privateMetadata?: any; _raw?: any } | null) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash# Verify current `any` usage in the touched sanitizer and related test file.
rg -nP --type=ts '\bany\b' packages/backend/src/util/decorateObjectWithResources.ts packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts

Repository: clerk/javascript

Length of output: 1441


🏁 Script executed:

#!/bin/bashset -euo pipefail
sed -n '40,100p' packages/backend/src/util/decorateObjectWithResources.ts | cat -n

Repository: clerk/javascript

Length of output: 1648


Replace any types in prunePrivateMetadata input

packages/backend/src/util/decorateObjectWithResources.ts uses any for private_metadata, privateMetadata, and _raw in the sanitizer signature (line 16); replace with unknown/Record<string, unknown> to keep the sanitization type-safe.

Suggested typed alternative
-function prunePrivateMetadata(resource?: { private_metadata?: any; privateMetadata?: any; _raw?: any } | null) {+type PrunableResource = {+ private_metadata?: unknown;+ privateMetadata?: unknown;+ _raw?: Record<string, unknown>;+} | null;++function prunePrivateMetadata(resource?: PrunableResource) {
🤖 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 `@packages/backend/src/util/decorateObjectWithResources.ts` at line 55, The
parameter types for prunePrivateMetadata should avoid any: change the signature
to use unknown for scalar-ish fields and Record<string, unknown> for object-like
fields (e.g., replace private_metadata?: any, privateMetadata?: any, _raw?: any
with private_metadata?: unknown | Record<string, unknown>, privateMetadata?:
unknown | Record<string, unknown>, _raw?: unknown | Record<string, unknown>);
update prunePrivateMetadata to use narrowings/type guards (typeof checks and
Object.prototype.toString or Array.isArray where appropriate) or safe casts
before accessing properties so the sanitizer remains type-safe while preserving
existing behavior for private_metadata, privateMetadata, and _raw.

@jacekradko

Copy link
Copy Markdown
Contributor

AI finding (P1): stripPrivateDataFromObject currently removes top-level _raw.private_metadata, but UserJSON may include nested organization_memberships, and each membership can include its own private_metadata plus a nested organization.private_metadata. Those nested fields would still serialize into __clerk_ssr_state if present, so the SSR leak is only partially fixed.

Please recursively redact private_metadata / privateMetadata from the cloned _raw payload, or at least cover organization_memberships[*] and nested organization, and add a regression test for that shape.

Relevant spots:

  • packages/backend/src/util/decorateObjectWithResources.ts:70
  • packages/backend/src/api/resources/JSON.ts:683
  • packages/backend/src/api/resources/JSON.ts:498

…n SSR sanitizer
The previous fix only removed the top-level `_raw.private_metadata`, but a
`User`'s `_raw` payload nests further private metadata — each
`organization_memberships[*]` carries its own `private_metadata` plus a nested
`organization.private_metadata` — which still serialized into
`__clerk_ssr_state`.
Redact `private_metadata`/`privateMetadata` recursively on a deep clone of
`_raw` so nested fields are stripped while the original resource (and its `raw`
getter) is left untouched. Adds a regression test for the
`organization_memberships` shape.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: cc7bcbc

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
NameType
@clerk/backendPatch
@clerk/astroPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/honoPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/tanstack-react-startPatch
@clerk/testingPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@dominic-clerk

Copy link
Copy Markdown
ContributorAuthor

New commit addresses the finding

@jacekradkojacekradko left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

:shipit:

@dominic-clerk
dominic-clerk merged commit fb184de into mainJun 2, 2026
45 checks passed
@dominic-clerk
dominic-clerk deleted the dc-strip-raw-private-metadata branch June 2, 2026 15:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@dominic-clerk@jacekradko
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(backend): strip private_metadata from resource _raw in SSR sanitizer by dominic-clerk · Pull Request #8702 · clerk/javascript · GitHub
Skip to content

fix(backend): strip private_metadata from resource _raw in SSR sanitizer - #8702

Merged
dominic-clerk merged 2 commits into
mainfrom
dc-strip-raw-private-metadata
Jun 2, 2026
Merged

fix(backend): strip private_metadata from resource _raw in SSR sanitizer#8702
dominic-clerk merged 2 commits into
mainfrom
dc-strip-raw-private-metadata

Conversation

@dominic-clerk

Copy link
Copy Markdown
Contributor

Description

stripPrivateDataFromObject now removes private_metadata from the backend resource _raw payload, preventing it from leaking into __clerk_ssr_state when a User/Organization resource is passed to buildClerkProps.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

stripPrivateDataFromObject now removes private_metadata from the backend
resource _raw payload, preventing it from leaking into __clerk_ssr_state
when a User/Organization resource is passed to buildClerkProps.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercelBot commented May 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentMay 29, 2026 4:34pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

Snapi: no API changes detected in @clerk/backend, @clerk/clerk-js, @clerk/nextjs, @clerk/react, @clerk/shared, @clerk/ui.

@pkg-pr-new

pkg-pr-newBot commented May 29, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@8702

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@8702

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@8702

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@8702

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@8702

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@8702

@clerk/express

npm i https://pkg.pr.new/@clerk/express@8702

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@8702

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@8702

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@8702

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@8702

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@8702

@clerk/react

npm i https://pkg.pr.new/@clerk/react@8702

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@8702

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@8702

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@8702

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@8702

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@8702

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@8702

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@8702

commit: cc7bcbc

@coderabbitai

coderabbitaiBot commented May 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 61e1e263-b0fd-4cd9-b22c-41a8e217599e

📥 Commits

Reviewing files that changed from the base of the PR and between 33198d5 and cc7bcbc.

📒 Files selected for processing (2)
  • packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts
  • packages/backend/src/util/decorateObjectWithResources.ts

📝 Walkthrough

Walkthrough

This PR expands private-data stripping to backend resource _raw payloads: the prune helper accepts optional _raw and uses redactPrivateMetadataDeep to recursively remove both private_metadata and privateMetadata keys from arrays and objects, replacing resource._raw with a sanitized clone. Tests confirm top-level and nested private keys are removed (including inside organization_memberships), public_metadata is preserved, and the original raw objects are not mutated. A changeset documents the fix to prevent leakage into __clerk_ssr_state during buildClerkProps.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly and specifically describes the main change: adding private_metadata stripping from resource _raw in the SSR sanitizer function.
Description check✅ PassedThe description is directly related to the changeset, explaining the bug fix and its purpose of preventing private_metadata leaks in __clerk_ssr_state.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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


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.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts (1)

10-11: ⚡ Quick win

Reduce as any usage in tests for safer fixtures.

These casts can be replaced with typed fixtures (e.g., Parameters<typeof User.fromJSON>[0] / Parameters<typeof Organization.fromJSON>[0]) to keep test intent while preserving type checks.

As per coding guidelines: "**/*.{ts,tsx}: Avoid any type - prefer unknown when type is uncertain, then narrow with type guards" and "No any types without justification in code review".

Also applies to: 29-29, 38-38, 47-48, 51-52, 65-65

🤖 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 `@packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts`
around lines 10 - 11, Replace the unsafe "as any" casts in the test fixtures
with properly typed fixture objects by using the parameter types of the model
deserializers (e.g., use Parameters<typeof User.fromJSON>[0] for the user
fixture and Parameters<typeof Organization.fromJSON>[0] for the organization
fixture) and adjust other occurrences similarly (lines referencing the same test
fixtures at the other locations) so the test inputs retain type checking; update
the test variable declarations to use those parameter types and pass the objects
through the existing fromJSON constructors (User.fromJSON,
Organization.fromJSON) or narrow with appropriate type guards instead of casting
to any.
🤖 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 `@packages/backend/src/util/decorateObjectWithResources.ts`:
- Line 55: The parameter types for prunePrivateMetadata should avoid any: change
the signature to use unknown for scalar-ish fields and Record<string, unknown>
for object-like fields (e.g., replace private_metadata?: any, privateMetadata?:
any, _raw?: any with private_metadata?: unknown | Record<string, unknown>,
privateMetadata?: unknown | Record<string, unknown>, _raw?: unknown |
Record<string, unknown>); update prunePrivateMetadata to use narrowings/type
guards (typeof checks and Object.prototype.toString or Array.isArray where
appropriate) or safe casts before accessing properties so the sanitizer remains
type-safe while preserving existing behavior for private_metadata,
privateMetadata, and _raw.
---
Nitpick comments:
In `@packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts`:
- Around line 10-11: Replace the unsafe "as any" casts in the test fixtures with
properly typed fixture objects by using the parameter types of the model
deserializers (e.g., use Parameters<typeof User.fromJSON>[0] for the user
fixture and Parameters<typeof Organization.fromJSON>[0] for the organization
fixture) and adjust other occurrences similarly (lines referencing the same test
fixtures at the other locations) so the test inputs retain type checking; update
the test variable declarations to use those parameter types and pass the objects
through the existing fromJSON constructors (User.fromJSON,
Organization.fromJSON) or narrow with appropriate type guards instead of casting
to any.
🪄 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: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: b1fc3d27-ae40-4507-881c-74abc9301b8e

📥 Commits

Reviewing files that changed from the base of the PR and between b9eb19d and 33198d5.

📒 Files selected for processing (3)
  • .changeset/proud-lions-allow.md
  • packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts
  • packages/backend/src/util/decorateObjectWithResources.ts

}

function prunePrivateMetadata(resource?: { private_metadata: any } | { privateMetadata: any } | null) {
function prunePrivateMetadata(resource?: { private_metadata?: any; privateMetadata?: any; _raw?: any } | null) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash# Verify current `any` usage in the touched sanitizer and related test file.
rg -nP --type=ts '\bany\b' packages/backend/src/util/decorateObjectWithResources.ts packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts

Repository: clerk/javascript

Length of output: 1441


🏁 Script executed:

#!/bin/bashset -euo pipefail
sed -n '40,100p' packages/backend/src/util/decorateObjectWithResources.ts | cat -n

Repository: clerk/javascript

Length of output: 1648


Replace any types in prunePrivateMetadata input

packages/backend/src/util/decorateObjectWithResources.ts uses any for private_metadata, privateMetadata, and _raw in the sanitizer signature (line 16); replace with unknown/Record<string, unknown> to keep the sanitization type-safe.

Suggested typed alternative
-function prunePrivateMetadata(resource?: { private_metadata?: any; privateMetadata?: any; _raw?: any } | null) {+type PrunableResource = {+ private_metadata?: unknown;+ privateMetadata?: unknown;+ _raw?: Record<string, unknown>;+} | null;++function prunePrivateMetadata(resource?: PrunableResource) {
🤖 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 `@packages/backend/src/util/decorateObjectWithResources.ts` at line 55, The
parameter types for prunePrivateMetadata should avoid any: change the signature
to use unknown for scalar-ish fields and Record<string, unknown> for object-like
fields (e.g., replace private_metadata?: any, privateMetadata?: any, _raw?: any
with private_metadata?: unknown | Record<string, unknown>, privateMetadata?:
unknown | Record<string, unknown>, _raw?: unknown | Record<string, unknown>);
update prunePrivateMetadata to use narrowings/type guards (typeof checks and
Object.prototype.toString or Array.isArray where appropriate) or safe casts
before accessing properties so the sanitizer remains type-safe while preserving
existing behavior for private_metadata, privateMetadata, and _raw.

@jacekradko

Copy link
Copy Markdown
Contributor

AI finding (P1): stripPrivateDataFromObject currently removes top-level _raw.private_metadata, but UserJSON may include nested organization_memberships, and each membership can include its own private_metadata plus a nested organization.private_metadata. Those nested fields would still serialize into __clerk_ssr_state if present, so the SSR leak is only partially fixed.

Please recursively redact private_metadata / privateMetadata from the cloned _raw payload, or at least cover organization_memberships[*] and nested organization, and add a regression test for that shape.

Relevant spots:

  • packages/backend/src/util/decorateObjectWithResources.ts:70
  • packages/backend/src/api/resources/JSON.ts:683
  • packages/backend/src/api/resources/JSON.ts:498

…n SSR sanitizer
The previous fix only removed the top-level `_raw.private_metadata`, but a
`User`'s `_raw` payload nests further private metadata — each
`organization_memberships[*]` carries its own `private_metadata` plus a nested
`organization.private_metadata` — which still serialized into
`__clerk_ssr_state`.
Redact `private_metadata`/`privateMetadata` recursively on a deep clone of
`_raw` so nested fields are stripped while the original resource (and its `raw`
getter) is left untouched. Adds a regression test for the
`organization_memberships` shape.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: cc7bcbc

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
NameType
@clerk/backendPatch
@clerk/astroPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/honoPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/tanstack-react-startPatch
@clerk/testingPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@dominic-clerk

Copy link
Copy Markdown
ContributorAuthor

New commit addresses the finding

@jacekradkojacekradko left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

:shipit:

@dominic-clerk
dominic-clerk merged commit fb184de into mainJun 2, 2026
45 checks passed
@dominic-clerk
dominic-clerk deleted the dc-strip-raw-private-metadata branch June 2, 2026 15:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@dominic-clerk@jacekradko
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(backend): strip private_metadata from resource _raw in SSR sanitizer by dominic-clerk · Pull Request #8702 · clerk/javascript · GitHub
Skip to content

fix(backend): strip private_metadata from resource _raw in SSR sanitizer - #8702

Merged
dominic-clerk merged 2 commits into
mainfrom
dc-strip-raw-private-metadata
Jun 2, 2026
Merged

fix(backend): strip private_metadata from resource _raw in SSR sanitizer#8702
dominic-clerk merged 2 commits into
mainfrom
dc-strip-raw-private-metadata

Conversation

@dominic-clerk

Copy link
Copy Markdown
Contributor

Description

stripPrivateDataFromObject now removes private_metadata from the backend resource _raw payload, preventing it from leaking into __clerk_ssr_state when a User/Organization resource is passed to buildClerkProps.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

stripPrivateDataFromObject now removes private_metadata from the backend
resource _raw payload, preventing it from leaking into __clerk_ssr_state
when a User/Organization resource is passed to buildClerkProps.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercelBot commented May 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentMay 29, 2026 4:34pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

Snapi: no API changes detected in @clerk/backend, @clerk/clerk-js, @clerk/nextjs, @clerk/react, @clerk/shared, @clerk/ui.

@pkg-pr-new

pkg-pr-newBot commented May 29, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@8702

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@8702

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@8702

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@8702

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@8702

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@8702

@clerk/express

npm i https://pkg.pr.new/@clerk/express@8702

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@8702

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@8702

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@8702

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@8702

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@8702

@clerk/react

npm i https://pkg.pr.new/@clerk/react@8702

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@8702

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@8702

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@8702

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@8702

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@8702

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@8702

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@8702

commit: cc7bcbc

@coderabbitai

coderabbitaiBot commented May 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 61e1e263-b0fd-4cd9-b22c-41a8e217599e

📥 Commits

Reviewing files that changed from the base of the PR and between 33198d5 and cc7bcbc.

📒 Files selected for processing (2)
  • packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts
  • packages/backend/src/util/decorateObjectWithResources.ts

📝 Walkthrough

Walkthrough

This PR expands private-data stripping to backend resource _raw payloads: the prune helper accepts optional _raw and uses redactPrivateMetadataDeep to recursively remove both private_metadata and privateMetadata keys from arrays and objects, replacing resource._raw with a sanitized clone. Tests confirm top-level and nested private keys are removed (including inside organization_memberships), public_metadata is preserved, and the original raw objects are not mutated. A changeset documents the fix to prevent leakage into __clerk_ssr_state during buildClerkProps.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly and specifically describes the main change: adding private_metadata stripping from resource _raw in the SSR sanitizer function.
Description check✅ PassedThe description is directly related to the changeset, explaining the bug fix and its purpose of preventing private_metadata leaks in __clerk_ssr_state.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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


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.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts (1)

10-11: ⚡ Quick win

Reduce as any usage in tests for safer fixtures.

These casts can be replaced with typed fixtures (e.g., Parameters<typeof User.fromJSON>[0] / Parameters<typeof Organization.fromJSON>[0]) to keep test intent while preserving type checks.

As per coding guidelines: "**/*.{ts,tsx}: Avoid any type - prefer unknown when type is uncertain, then narrow with type guards" and "No any types without justification in code review".

Also applies to: 29-29, 38-38, 47-48, 51-52, 65-65

🤖 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 `@packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts`
around lines 10 - 11, Replace the unsafe "as any" casts in the test fixtures
with properly typed fixture objects by using the parameter types of the model
deserializers (e.g., use Parameters<typeof User.fromJSON>[0] for the user
fixture and Parameters<typeof Organization.fromJSON>[0] for the organization
fixture) and adjust other occurrences similarly (lines referencing the same test
fixtures at the other locations) so the test inputs retain type checking; update
the test variable declarations to use those parameter types and pass the objects
through the existing fromJSON constructors (User.fromJSON,
Organization.fromJSON) or narrow with appropriate type guards instead of casting
to any.
🤖 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 `@packages/backend/src/util/decorateObjectWithResources.ts`:
- Line 55: The parameter types for prunePrivateMetadata should avoid any: change
the signature to use unknown for scalar-ish fields and Record<string, unknown>
for object-like fields (e.g., replace private_metadata?: any, privateMetadata?:
any, _raw?: any with private_metadata?: unknown | Record<string, unknown>,
privateMetadata?: unknown | Record<string, unknown>, _raw?: unknown |
Record<string, unknown>); update prunePrivateMetadata to use narrowings/type
guards (typeof checks and Object.prototype.toString or Array.isArray where
appropriate) or safe casts before accessing properties so the sanitizer remains
type-safe while preserving existing behavior for private_metadata,
privateMetadata, and _raw.
---
Nitpick comments:
In `@packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts`:
- Around line 10-11: Replace the unsafe "as any" casts in the test fixtures with
properly typed fixture objects by using the parameter types of the model
deserializers (e.g., use Parameters<typeof User.fromJSON>[0] for the user
fixture and Parameters<typeof Organization.fromJSON>[0] for the organization
fixture) and adjust other occurrences similarly (lines referencing the same test
fixtures at the other locations) so the test inputs retain type checking; update
the test variable declarations to use those parameter types and pass the objects
through the existing fromJSON constructors (User.fromJSON,
Organization.fromJSON) or narrow with appropriate type guards instead of casting
to any.
🪄 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: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: b1fc3d27-ae40-4507-881c-74abc9301b8e

📥 Commits

Reviewing files that changed from the base of the PR and between b9eb19d and 33198d5.

📒 Files selected for processing (3)
  • .changeset/proud-lions-allow.md
  • packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts
  • packages/backend/src/util/decorateObjectWithResources.ts

}

function prunePrivateMetadata(resource?: { private_metadata: any } | { privateMetadata: any } | null) {
function prunePrivateMetadata(resource?: { private_metadata?: any; privateMetadata?: any; _raw?: any } | null) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash# Verify current `any` usage in the touched sanitizer and related test file.
rg -nP --type=ts '\bany\b' packages/backend/src/util/decorateObjectWithResources.ts packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts

Repository: clerk/javascript

Length of output: 1441


🏁 Script executed:

#!/bin/bashset -euo pipefail
sed -n '40,100p' packages/backend/src/util/decorateObjectWithResources.ts | cat -n

Repository: clerk/javascript

Length of output: 1648


Replace any types in prunePrivateMetadata input

packages/backend/src/util/decorateObjectWithResources.ts uses any for private_metadata, privateMetadata, and _raw in the sanitizer signature (line 16); replace with unknown/Record<string, unknown> to keep the sanitization type-safe.

Suggested typed alternative
-function prunePrivateMetadata(resource?: { private_metadata?: any; privateMetadata?: any; _raw?: any } | null) {+type PrunableResource = {+ private_metadata?: unknown;+ privateMetadata?: unknown;+ _raw?: Record<string, unknown>;+} | null;++function prunePrivateMetadata(resource?: PrunableResource) {
🤖 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 `@packages/backend/src/util/decorateObjectWithResources.ts` at line 55, The
parameter types for prunePrivateMetadata should avoid any: change the signature
to use unknown for scalar-ish fields and Record<string, unknown> for object-like
fields (e.g., replace private_metadata?: any, privateMetadata?: any, _raw?: any
with private_metadata?: unknown | Record<string, unknown>, privateMetadata?:
unknown | Record<string, unknown>, _raw?: unknown | Record<string, unknown>);
update prunePrivateMetadata to use narrowings/type guards (typeof checks and
Object.prototype.toString or Array.isArray where appropriate) or safe casts
before accessing properties so the sanitizer remains type-safe while preserving
existing behavior for private_metadata, privateMetadata, and _raw.

@jacekradko

Copy link
Copy Markdown
Contributor

AI finding (P1): stripPrivateDataFromObject currently removes top-level _raw.private_metadata, but UserJSON may include nested organization_memberships, and each membership can include its own private_metadata plus a nested organization.private_metadata. Those nested fields would still serialize into __clerk_ssr_state if present, so the SSR leak is only partially fixed.

Please recursively redact private_metadata / privateMetadata from the cloned _raw payload, or at least cover organization_memberships[*] and nested organization, and add a regression test for that shape.

Relevant spots:

  • packages/backend/src/util/decorateObjectWithResources.ts:70
  • packages/backend/src/api/resources/JSON.ts:683
  • packages/backend/src/api/resources/JSON.ts:498

…n SSR sanitizer
The previous fix only removed the top-level `_raw.private_metadata`, but a
`User`'s `_raw` payload nests further private metadata — each
`organization_memberships[*]` carries its own `private_metadata` plus a nested
`organization.private_metadata` — which still serialized into
`__clerk_ssr_state`.
Redact `private_metadata`/`privateMetadata` recursively on a deep clone of
`_raw` so nested fields are stripped while the original resource (and its `raw`
getter) is left untouched. Adds a regression test for the
`organization_memberships` shape.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: cc7bcbc

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
NameType
@clerk/backendPatch
@clerk/astroPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/honoPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/tanstack-react-startPatch
@clerk/testingPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@dominic-clerk

Copy link
Copy Markdown
ContributorAuthor

New commit addresses the finding

@jacekradkojacekradko left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

:shipit:

@dominic-clerk
dominic-clerk merged commit fb184de into mainJun 2, 2026
45 checks passed
@dominic-clerk
dominic-clerk deleted the dc-strip-raw-private-metadata branch June 2, 2026 15:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@dominic-clerk@jacekradko
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); fix(backend): strip private_metadata from resource _raw in SSR sanitizer by dominic-clerk · Pull Request #8702 · clerk/javascript · GitHub
Skip to content

fix(backend): strip private_metadata from resource _raw in SSR sanitizer - #8702

Merged
dominic-clerk merged 2 commits into
mainfrom
dc-strip-raw-private-metadata
Jun 2, 2026
Merged

fix(backend): strip private_metadata from resource _raw in SSR sanitizer#8702
dominic-clerk merged 2 commits into
mainfrom
dc-strip-raw-private-metadata

Conversation

@dominic-clerk

Copy link
Copy Markdown
Contributor

Description

stripPrivateDataFromObject now removes private_metadata from the backend resource _raw payload, preventing it from leaking into __clerk_ssr_state when a User/Organization resource is passed to buildClerkProps.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

stripPrivateDataFromObject now removes private_metadata from the backend
resource _raw payload, preventing it from leaking into __clerk_ssr_state
when a User/Organization resource is passed to buildClerkProps.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercelBot commented May 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentMay 29, 2026 4:34pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

Snapi: no API changes detected in @clerk/backend, @clerk/clerk-js, @clerk/nextjs, @clerk/react, @clerk/shared, @clerk/ui.

@pkg-pr-new

pkg-pr-newBot commented May 29, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@8702

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@8702

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@8702

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@8702

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@8702

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@8702

@clerk/express

npm i https://pkg.pr.new/@clerk/express@8702

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@8702

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@8702

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@8702

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@8702

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@8702

@clerk/react

npm i https://pkg.pr.new/@clerk/react@8702

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@8702

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@8702

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@8702

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@8702

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@8702

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@8702

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@8702

commit: cc7bcbc

@coderabbitai

coderabbitaiBot commented May 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 61e1e263-b0fd-4cd9-b22c-41a8e217599e

📥 Commits

Reviewing files that changed from the base of the PR and between 33198d5 and cc7bcbc.

📒 Files selected for processing (2)
  • packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts
  • packages/backend/src/util/decorateObjectWithResources.ts

📝 Walkthrough

Walkthrough

This PR expands private-data stripping to backend resource _raw payloads: the prune helper accepts optional _raw and uses redactPrivateMetadataDeep to recursively remove both private_metadata and privateMetadata keys from arrays and objects, replacing resource._raw with a sanitized clone. Tests confirm top-level and nested private keys are removed (including inside organization_memberships), public_metadata is preserved, and the original raw objects are not mutated. A changeset documents the fix to prevent leakage into __clerk_ssr_state during buildClerkProps.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly and specifically describes the main change: adding private_metadata stripping from resource _raw in the SSR sanitizer function.
Description check✅ PassedThe description is directly related to the changeset, explaining the bug fix and its purpose of preventing private_metadata leaks in __clerk_ssr_state.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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


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.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts (1)

10-11: ⚡ Quick win

Reduce as any usage in tests for safer fixtures.

These casts can be replaced with typed fixtures (e.g., Parameters<typeof User.fromJSON>[0] / Parameters<typeof Organization.fromJSON>[0]) to keep test intent while preserving type checks.

As per coding guidelines: "**/*.{ts,tsx}: Avoid any type - prefer unknown when type is uncertain, then narrow with type guards" and "No any types without justification in code review".

Also applies to: 29-29, 38-38, 47-48, 51-52, 65-65

🤖 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 `@packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts`
around lines 10 - 11, Replace the unsafe "as any" casts in the test fixtures
with properly typed fixture objects by using the parameter types of the model
deserializers (e.g., use Parameters<typeof User.fromJSON>[0] for the user
fixture and Parameters<typeof Organization.fromJSON>[0] for the organization
fixture) and adjust other occurrences similarly (lines referencing the same test
fixtures at the other locations) so the test inputs retain type checking; update
the test variable declarations to use those parameter types and pass the objects
through the existing fromJSON constructors (User.fromJSON,
Organization.fromJSON) or narrow with appropriate type guards instead of casting
to any.
🤖 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 `@packages/backend/src/util/decorateObjectWithResources.ts`:
- Line 55: The parameter types for prunePrivateMetadata should avoid any: change
the signature to use unknown for scalar-ish fields and Record<string, unknown>
for object-like fields (e.g., replace private_metadata?: any, privateMetadata?:
any, _raw?: any with private_metadata?: unknown | Record<string, unknown>,
privateMetadata?: unknown | Record<string, unknown>, _raw?: unknown |
Record<string, unknown>); update prunePrivateMetadata to use narrowings/type
guards (typeof checks and Object.prototype.toString or Array.isArray where
appropriate) or safe casts before accessing properties so the sanitizer remains
type-safe while preserving existing behavior for private_metadata,
privateMetadata, and _raw.
---
Nitpick comments:
In `@packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts`:
- Around line 10-11: Replace the unsafe "as any" casts in the test fixtures with
properly typed fixture objects by using the parameter types of the model
deserializers (e.g., use Parameters<typeof User.fromJSON>[0] for the user
fixture and Parameters<typeof Organization.fromJSON>[0] for the organization
fixture) and adjust other occurrences similarly (lines referencing the same test
fixtures at the other locations) so the test inputs retain type checking; update
the test variable declarations to use those parameter types and pass the objects
through the existing fromJSON constructors (User.fromJSON,
Organization.fromJSON) or narrow with appropriate type guards instead of casting
to any.
🪄 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: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: b1fc3d27-ae40-4507-881c-74abc9301b8e

📥 Commits

Reviewing files that changed from the base of the PR and between b9eb19d and 33198d5.

📒 Files selected for processing (3)
  • .changeset/proud-lions-allow.md
  • packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts
  • packages/backend/src/util/decorateObjectWithResources.ts

}

function prunePrivateMetadata(resource?: { private_metadata: any } | { privateMetadata: any } | null) {
function prunePrivateMetadata(resource?: { private_metadata?: any; privateMetadata?: any; _raw?: any } | null) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash# Verify current `any` usage in the touched sanitizer and related test file.
rg -nP --type=ts '\bany\b' packages/backend/src/util/decorateObjectWithResources.ts packages/backend/src/util/__tests__/decorateObjectWithResources.test.ts

Repository: clerk/javascript

Length of output: 1441


🏁 Script executed:

#!/bin/bashset -euo pipefail
sed -n '40,100p' packages/backend/src/util/decorateObjectWithResources.ts | cat -n

Repository: clerk/javascript

Length of output: 1648


Replace any types in prunePrivateMetadata input

packages/backend/src/util/decorateObjectWithResources.ts uses any for private_metadata, privateMetadata, and _raw in the sanitizer signature (line 16); replace with unknown/Record<string, unknown> to keep the sanitization type-safe.

Suggested typed alternative
-function prunePrivateMetadata(resource?: { private_metadata?: any; privateMetadata?: any; _raw?: any } | null) {+type PrunableResource = {+ private_metadata?: unknown;+ privateMetadata?: unknown;+ _raw?: Record<string, unknown>;+} | null;++function prunePrivateMetadata(resource?: PrunableResource) {
🤖 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 `@packages/backend/src/util/decorateObjectWithResources.ts` at line 55, The
parameter types for prunePrivateMetadata should avoid any: change the signature
to use unknown for scalar-ish fields and Record<string, unknown> for object-like
fields (e.g., replace private_metadata?: any, privateMetadata?: any, _raw?: any
with private_metadata?: unknown | Record<string, unknown>, privateMetadata?:
unknown | Record<string, unknown>, _raw?: unknown | Record<string, unknown>);
update prunePrivateMetadata to use narrowings/type guards (typeof checks and
Object.prototype.toString or Array.isArray where appropriate) or safe casts
before accessing properties so the sanitizer remains type-safe while preserving
existing behavior for private_metadata, privateMetadata, and _raw.

@jacekradko

Copy link
Copy Markdown
Contributor

AI finding (P1): stripPrivateDataFromObject currently removes top-level _raw.private_metadata, but UserJSON may include nested organization_memberships, and each membership can include its own private_metadata plus a nested organization.private_metadata. Those nested fields would still serialize into __clerk_ssr_state if present, so the SSR leak is only partially fixed.

Please recursively redact private_metadata / privateMetadata from the cloned _raw payload, or at least cover organization_memberships[*] and nested organization, and add a regression test for that shape.

Relevant spots:

  • packages/backend/src/util/decorateObjectWithResources.ts:70
  • packages/backend/src/api/resources/JSON.ts:683
  • packages/backend/src/api/resources/JSON.ts:498

…n SSR sanitizer
The previous fix only removed the top-level `_raw.private_metadata`, but a
`User`'s `_raw` payload nests further private metadata — each
`organization_memberships[*]` carries its own `private_metadata` plus a nested
`organization.private_metadata` — which still serialized into
`__clerk_ssr_state`.
Redact `private_metadata`/`privateMetadata` recursively on a deep clone of
`_raw` so nested fields are stripped while the original resource (and its `raw`
getter) is left untouched. Adds a regression test for the
`organization_memberships` shape.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: cc7bcbc

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
NameType
@clerk/backendPatch
@clerk/astroPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/honoPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/tanstack-react-startPatch
@clerk/testingPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@dominic-clerk

Copy link
Copy Markdown
ContributorAuthor

New commit addresses the finding

@jacekradkojacekradko left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

:shipit:

@dominic-clerk
dominic-clerk merged commit fb184de into mainJun 2, 2026
45 checks passed
@dominic-clerk
dominic-clerk deleted the dc-strip-raw-private-metadata branch June 2, 2026 15:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@dominic-clerk@jacekradko