Skip to content

fix: stop object reference leak in AppFabricationFulfillment#36943

Merged
kodiakhq[bot] merged 9 commits intodevelopfrom
fix/app-fabrication-fulfillment-leak
Sep 22, 2025
Merged

fix: stop object reference leak in AppFabricationFulfillment#36943
kodiakhq[bot] merged 9 commits intodevelopfrom
fix/app-fabrication-fulfillment-leak

Conversation

@d-gubert
Copy link
Member

@d-gubert d-gubert commented Sep 15, 2025

Proposed changes (including videos or screenshots)

During app installation and update, the AppFabricationFulfillment object that the AppManager returns will actually leak a direct reference to an IAppInfo object that is used inside the apps-engine scope.

This means that when server code interacts with that object, it will modify the object that is stored in the database. Saving data outside of the schema in the database can cause signature mismatch.

Issue(s)

Steps to test or reproduce

Further comments

ARCH-1804

Summary by CodeRabbit

  • Bug Fixes
    • Fixed a data-integrity issue during app installation by ensuring app metadata and implemented interfaces are stored as independent copies, preventing later mutations from altering saved data.
  • Tests
    • Added unit tests verifying stored app info and implemented interfaces remain unchanged when the original inputs are mutated.
  • Chores
    • Prepared patch releases for affected packages to deliver the fix.

@changeset-bot
Copy link

changeset-bot bot commented Sep 15, 2025

🦋 Changeset detected

Latest commit: 59684fa

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

This PR includes changesets to release 40 packages
Name Type
@rocket.chat/apps-engine Patch
@rocket.chat/meteor Patch
@rocket.chat/apps Patch
@rocket.chat/core-services Patch
@rocket.chat/core-typings Patch
@rocket.chat/fuselage-ui-kit Patch
@rocket.chat/rest-typings Patch
@rocket.chat/ddp-streamer Patch
@rocket.chat/presence Patch
rocketchat-services Patch
@rocket.chat/uikit-playground Patch
@rocket.chat/api-client Patch
@rocket.chat/cron Patch
@rocket.chat/ddp-client Patch
@rocket.chat/freeswitch Patch
@rocket.chat/gazzodown Patch
@rocket.chat/http-router Patch
@rocket.chat/livechat Patch
@rocket.chat/model-typings Patch
@rocket.chat/ui-avatar Patch
@rocket.chat/ui-client Patch
@rocket.chat/ui-contexts Patch
@rocket.chat/web-ui-registration Patch
@rocket.chat/account-service Patch
@rocket.chat/authorization-service Patch
@rocket.chat/omnichannel-transcript Patch
@rocket.chat/presence-service Patch
@rocket.chat/queue-worker Patch
@rocket.chat/stream-hub-service Patch
@rocket.chat/license Patch
@rocket.chat/omnichannel-services Patch
@rocket.chat/pdf-worker Patch
@rocket.chat/models Patch
@rocket.chat/network-broker Patch
@rocket.chat/omni-core-ee Patch
@rocket.chat/mock-providers Patch
@rocket.chat/ui-video-conf Patch
@rocket.chat/ui-voip Patch
@rocket.chat/instance-status Patch
@rocket.chat/omni-core Patch

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

@dionisio-bot
Copy link
Contributor

dionisio-bot bot commented Sep 15, 2025

Looks like this PR is ready to merge! 🎉
If you have any trouble, please check the PR guidelines

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 15, 2025

Caution

Review failed

The head commit changed during the review from 083ece0 to 6409c4f.

Walkthrough

Replaced internal-state assignments with deep copies via structuredClone in AppFabricationFulfillment setters, added unit tests validating deep-clone semantics (including empty-object handling), and added a changeset entry; no public API or signature changes.

Changes

Cohort / File(s) Summary of changes
AppFabricationFulfillment implementation
packages/apps-engine/src/server/compiler/AppFabricationFulfillment.ts
Replaced direct assignments in setAppInfo and setImplementedInterfaces with structuredClone to store independent deep copies; preserved existing control flow and signatures.
Unit tests
packages/apps-engine/tests/server/compiler/AppFabricationFulfillment.spec.ts
Added tests asserting deep-clone behavior for setAppInfo and setImplementedInterfaces, including empty-object handling; added AppPermissions import; existing tests retained.
Changeset entry
.changeset/tasty-ravens-grow.md
Added changeset declaring patch releases for @rocket.chat/apps-engine and @rocket.chat/meteor describing bug fix for object reference leak during app installation.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Caller
  participant AFF as AppFabricationFulfillment
  participant Clone as structuredClone

  Caller->>AFF: setAppInfo(info)
  AFF->>Clone: structuredClone(info)
  Clone-->>AFF: deepCopy
  AFF-->>Caller: stored deep copy

  Caller->>AFF: getAppInfo()
  AFF-->>Caller: stored deep copy

  Caller->>AFF: setImplementedInterfaces(map)
  AFF->>Clone: structuredClone(map)
  Clone-->>AFF: deepCopy
  AFF-->>Caller: stored deep copy

  Caller->>AFF: getImplementedInterfaces()
  AFF-->>Caller: stored deep copy
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10–15 minutes

Poem

I hop and clone with careful paw,
No shared refs to cause a flaw.
Tests nibble, ensure the seam—
Objects safe, no haunted dream.
A carrot toast to tidy code. 🥕

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "fix: stop object reference leak in AppFabricationFulfillment" is concise, directly describes the primary change, and matches the changeset intent to prevent leaking internal object references from AppFabricationFulfillment. It is specific and readable for a reviewer scanning PR history.
Linked Issues Check ✅ Passed The changes replace direct assignments with structuredClone in setAppInfo and setImplementedInterfaces and add unit tests that assert deep-clone semantics, which prevents exposing internal IAppInfo references and addresses the linked issue's goals to avoid unintended mutations and invalid persisted data. The included .changeset documents a patch release for the affected packages, aligning with the remediation plan. Based on the provided summaries, the code-level objectives from ARCH-1804 are satisfied.
Out of Scope Changes Check ✅ Passed All modifications are confined to AppFabricationFulfillment implementation, corresponding tests, and a .changeset entry; there are no edits to unrelated modules or public API signatures in the provided summary, so no out-of-scope changes are evident. The diffs align with the PR objectives to stop the object reference leak.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
packages/apps-engine/src/server/compiler/AppFabricationFulfillment.ts (1)

41-42: Shallow copy is sufficient for a boolean map; structuredClone is overkill here.

This object is one level deep with primitive values. A spread copy avoids the heavier deep‑clone and is clearer.

-		this.implemented = structuredClone(interfaces);
+		this.implemented = { ...interfaces };

Side note: method name getImplementedInferfaces() contains a typo. Consider adding a correctly spelled alias (getImplementedInterfaces) and deprecating the old one in a follow‑up to avoid a breaking change.

packages/apps-engine/tests/server/compiler/AppFabricationFulfillment.spec.ts (2)

56-99: Strengthen the test by asserting reference inequality (true deep clone).

Add identity checks so the test fails if a shallow copy slips in.

 		const aff = new AppFabricationFulfillment();
 		aff.setAppInfo(originalInfo);

 		// Verify the stored info is equal to the original
 		Expect(aff.getAppInfo()).toEqual(originalInfo);
+		// And not the same references (deep clone)
+		Expect(aff.getAppInfo()).not.toBe(originalInfo);
+		Expect(aff.getAppInfo().author).not.toBe(originalInfo.author);
+		Expect(aff.getAppInfo().implements).not.toBe(originalInfo.implements);
+		Expect(aff.getAppInfo().permissions).not.toBe(originalInfo.permissions);

100-127: Add an identity check for the interfaces map; consider naming fix follow‑up.

Validate it’s not the same reference. Also consider adding a new getImplementedInterfaces() alias and deprecating the misspelled one.

 		// Verify the stored interfaces are equal to the original
 		Expect(aff.getImplementedInferfaces()).toEqual(originalInterfaces);
+		// And not the same reference
+		Expect(aff.getImplementedInferfaces()).not.toBe(originalInterfaces);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c5ee569 and 738861b.

📒 Files selected for processing (2)
  • packages/apps-engine/src/server/compiler/AppFabricationFulfillment.ts (2 hunks)
  • packages/apps-engine/tests/server/compiler/AppFabricationFulfillment.spec.ts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/apps-engine/tests/server/compiler/AppFabricationFulfillment.spec.ts (2)
packages/apps-engine/src/server/permissions/AppPermissions.ts (1)
  • AppPermissions (24-125)
packages/apps-engine/src/server/compiler/AppFabricationFulfillment.ts (1)
  • AppFabricationFulfillment (5-75)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: 📦 Build Packages
  • GitHub Check: Builds matrix rust bindings against alpine
  • GitHub Check: CodeQL-Build
  • GitHub Check: CodeQL-Build
🔇 Additional comments (2)
packages/apps-engine/tests/server/compiler/AppFabricationFulfillment.spec.ts (2)

11-11: LGTM: importing AppPermissions improves test realism.

Keeps the permissions payload representative of production data.


129-137: LGTM: empty object handling + non‑identity check.

Covers the edge case well; assertions are appropriate.

@d-gubert d-gubert added this to the 7.11.0 milestone Sep 15, 2025
@codecov
Copy link

codecov bot commented Sep 15, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 66.28%. Comparing base (5a503b4) to head (59684fa).
⚠️ Report is 1 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop   #36943      +/-   ##
===========================================
- Coverage    67.33%   66.28%   -1.05%     
===========================================
  Files         3339     3393      +54     
  Lines       113205   115451    +2246     
  Branches     20535    21165     +630     
===========================================
+ Hits         76224    76525     +301     
- Misses       34376    36312    +1936     
- Partials      2605     2614       +9     
Flag Coverage Δ
e2e 56.92% <ø> (-0.01%) ⬇️
unit 71.26% <ø> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

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

@d-gubert d-gubert marked this pull request as ready for review September 15, 2025 20:48
@d-gubert d-gubert requested a review from a team as a code owner September 15, 2025 20:48
@d-gubert d-gubert force-pushed the fix/app-fabrication-fulfillment-leak branch from 2a03f7b to 9dfb018 Compare September 16, 2025 18:58
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
packages/apps-engine/tests/server/compiler/AppFabricationFulfillment.spec.ts (2)

56-98: Strengthen deep‑clone assertions (identity + size checks).

Add reference and size assertions to make the clone guarantees explicit and avoid false positives if equality semantics change.

@@
   const aff = new AppFabricationFulfillment();
   aff.setAppInfo(originalInfo);
 
+  // Identity checks: internal copy must not alias the input
+  Expect(aff.getAppInfo()).not.toBe(originalInfo);
+  Expect(aff.getAppInfo().author).not.toBe(originalInfo.author);
+  Expect(aff.getAppInfo().implements).not.toBe(originalInfo.implements);
+  Expect(aff.getAppInfo().permissions).not.toBe(originalInfo.permissions);
@@
   originalInfo.implements.push(AppInterface.IPostMessageSent);
   originalInfo.permissions.push(AppPermissions.message.write);
@@
   Expect(aff.getAppInfo().permissions).not.toContain(AppPermissions.message.write);
+
+  // Sanity: sizes unchanged despite external mutations
+  Expect(aff.getAppInfo().implements.length).toEqual(1);
+  Expect(aff.getAppInfo().permissions.length).toEqual(2);

100-127: Also assert map identity and cardinality for implemented interfaces.

Add a non‑aliasing check and invariant on key count.

@@
   const aff = new AppFabricationFulfillment();
   aff.setImplementedInterfaces(originalInterfaces);
 
+  // Identity check: stored map must not alias the input
+  Expect(aff.getImplementedInferfaces()).not.toBe(originalInterfaces);
@@
   Expect(aff.getImplementedInferfaces()[AppInterface.IPreMessageSentModify]).not.toBeDefined();
+
+  // Sanity: key count unchanged
+  Expect(Object.keys(aff.getImplementedInferfaces()).length).toEqual(3);

Minor API nit: method name typo.

Consider adding an alias getImplementedInterfaces() (and deprecating the misspelled getImplementedInferfaces()), then update tests in a follow‑up.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2a03f7b and 9dfb018.

📒 Files selected for processing (3)
  • .changeset/tasty-ravens-grow.md (1 hunks)
  • packages/apps-engine/src/server/compiler/AppFabricationFulfillment.ts (2 hunks)
  • packages/apps-engine/tests/server/compiler/AppFabricationFulfillment.spec.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • .changeset/tasty-ravens-grow.md
  • packages/apps-engine/src/server/compiler/AppFabricationFulfillment.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/apps-engine/tests/server/compiler/AppFabricationFulfillment.spec.ts (2)
packages/apps-engine/src/server/permissions/AppPermissions.ts (1)
  • AppPermissions (24-125)
packages/apps-engine/src/server/compiler/AppFabricationFulfillment.ts (1)
  • AppFabricationFulfillment (5-75)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: CodeQL-Build
🔇 Additional comments (2)
packages/apps-engine/tests/server/compiler/AppFabricationFulfillment.spec.ts (2)

11-11: LGTM: importing AppPermissions for realistic fixtures.


129-137: LGTM: empty object handled and no aliasing.

ggazzo
ggazzo previously approved these changes Sep 18, 2025
@ggazzo ggazzo added the stat: QA assured Means it has been tested and approved by a company insider label Sep 18, 2025
@dionisio-bot dionisio-bot bot added the stat: ready to merge PR tested and approved waiting for merge label Sep 18, 2025
@kodiakhq kodiakhq bot removed the stat: ready to merge PR tested and approved waiting for merge label Sep 18, 2025
@kodiakhq
Copy link
Contributor

kodiakhq bot commented Sep 18, 2025

This PR currently has a merge conflict. Please resolve this and then re-add the ['stat: ready to merge', 'automerge'] label.

@d-gubert d-gubert added the stat: ready to merge PR tested and approved waiting for merge label Sep 19, 2025
@kodiakhq kodiakhq bot merged commit 72d9474 into develop Sep 22, 2025
48 of 49 checks passed
@kodiakhq kodiakhq bot deleted the fix/app-fabrication-fulfillment-leak branch September 22, 2025 16:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stat: QA assured Means it has been tested and approved by a company insider stat: ready to merge PR tested and approved waiting for merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants