Skip to content

Conversation

@yash-rajpal
Copy link
Member

@yash-rajpal yash-rajpal commented Jan 6, 2026

Proposed changes (including videos or screenshots)

Some issues were encountered with image orientation when Message_Attachments_Strip_Exif is enabled. This was because, exif metadata was being removed before re-orienting the image. This PR fixes this by changing the flow of processes. Now it first re-orients the image and later strips away the exif metadata.

Issue(s)

  • Enable setting Message_Attachments_Strip_Exif
  • Send an inverted or rotated image in a message attachment
  • image orientation after upload is wrong

Steps to test or reproduce

Further comments

CORE-1563

Only downside I see to this approach is not being much memory efficient, because we need to convert the image files to buffer and then we also need to maintain a second buffer of similar size to keep the stripped buffer.

Summary by CodeRabbit

  • Bug Fixes

    • Image attachments now respect the Message_Attachments_Strip_Exif setting: when enabled, sensitive EXIF metadata is stripped while preserving correct image orientation and display quality. Upload handling and logging around image reorientation have been improved for greater reliability.
  • Chores

    • Added a changeset to mark a patch release.

✏️ Tip: You can customize this high-level summary in your review settings.

@dionisio-bot
Copy link
Contributor

dionisio-bot bot commented Jan 6, 2026

Looks like this PR is not ready to merge, because of the following issues:

  • This PR has conflicts, please resolve them before merging
  • This PR is missing the 'stat: QA assured' label
  • This PR is not mergeable
  • This PR is targeting the wrong base branch. It should target 8.2.0, but it targets 8.1.0

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@changeset-bot
Copy link

changeset-bot bot commented Jan 6, 2026

🦋 Changeset detected

Latest commit: 4de70cd

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/meteor Patch
@rocket.chat/core-typings Patch
@rocket.chat/rest-typings Patch
@rocket.chat/uikit-playground Patch
@rocket.chat/api-client Patch
@rocket.chat/apps Patch
@rocket.chat/core-services Patch
@rocket.chat/cron Patch
@rocket.chat/ddp-client Patch
@rocket.chat/fuselage-ui-kit 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/ui-voip Patch
@rocket.chat/web-ui-registration Patch
@rocket.chat/account-service Patch
@rocket.chat/authorization-service Patch
@rocket.chat/ddp-streamer Patch
@rocket.chat/omnichannel-transcript Patch
@rocket.chat/presence-service Patch
@rocket.chat/queue-worker Patch
@rocket.chat/abac Patch
@rocket.chat/federation-matrix Patch
@rocket.chat/license Patch
@rocket.chat/media-calls Patch
@rocket.chat/omnichannel-services Patch
@rocket.chat/pdf-worker Patch
@rocket.chat/presence Patch
rocketchat-services 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/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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 6, 2026

Walkthrough

Moves EXIF-stripping out of the rooms.media route and into the FileUpload validation flow, making EXIF removal conditional on the Message_Attachments_Strip_Exif setting; adds ExifTransformer usage and temporary-file handling during stripping; updates a changeset documenting the patch.

Changes

Cohort / File(s) Summary
Changeset Documentation
/.changeset/dry-pumpkins-design.md
Adds a changeset describing a patch release for @rocket.chat/meteor addressing image orientation / EXIF handling.
Rooms API
apps/meteor/app/api/server/v1/rooms.ts
Removes EXIF-stripping code path and Media import from @rocket.chat/core-services; changes fileBuffer to const.
FileUpload EXIF Handling
apps/meteor/app/file-upload/server/lib/FileUpload.ts
Adds ExifTransformer and pipeline imports; implements conditional EXIF stripping during validation when Message_Attachments_Strip_Exif is enabled (writes stripped output to a temp file then renames), adds error handling and logging, and a console.trace('insert') in insert path.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant FileUploadService
  participant ExifTransformer
  participant TempFS
  participant Storage

  Client->>FileUploadService: Upload file (validate phase)
  FileUploadService->>FileUploadService: check Message_Attachments_Strip_Exif
  alt strip EXIF enabled
    FileUploadService->>TempFS: read tmpFile into stream
    TempFS->>ExifTransformer: pipe stream to ExifTransformer
    ExifTransformer->>TempFS: write stripped output to temp path
    TempFS->>FileUploadService: rename temp -> original tmpFile
  end
  FileUploadService->>Storage: proceed to store file / compute size
  FileUploadService->>Client: return upload result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

stat: ready to merge, stat: QA assured

Suggested reviewers

  • ggazzo
  • rodrigok

Poem

🐰 Hoppy hop, a pixeled fix in store,
I nudge the bytes and tumble through the door,
Strip a little EXIF, turn the image right,
Temp files hop back — now every photo's bright! ✨📸

🚥 Pre-merge checks | ✅ 5
✅ 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 accurately reflects the main objective of the PR - fixing image orientation issues by reordering EXIF processing logic.
Linked Issues check ✅ Passed The PR addresses CORE-1563 by reordering processing to apply EXIF-based reorientation before stripping EXIF metadata, ensuring correct image orientation.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing image orientation: moving EXIF stripping logic, importing necessary tools, and adding console tracing.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/image-orientation

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.

@yash-rajpal yash-rajpal marked this pull request as ready for review January 6, 2026 19:57
@yash-rajpal yash-rajpal requested a review from a team as a code owner January 6, 2026 19:57
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 3 files

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

🤖 Fix all issues with AI Agents
In @apps/meteor/app/file-upload/server/lib/FileUpload.ts:
- Around line 408-414: The EXIF stripping block uses blocking
fs.readFileSync/fs.writeFileSync and lacks error handling; replace these with
async fs/promises methods and wrap the operation in try/catch. Specifically,
inside the branch that checks settings.get('Message_Attachments_Strip_Exif') and
the code that calls Media.stripExifFromBuffer(tmpBuffer), use await
fsPromises.readFile(tmpFile) and await fsPromises.writeFile(tmpFile,
strippedFileBuffer) (or the already imported async fs binding used elsewhere),
and catch errors from readFile, Media.stripExifFromBuffer, and writeFile to
log/handle them and prevent crashing the upload; also replace the later
fs.lstatSync call with await fsPromises.lstat(tmpFile) for consistency.
📜 Review details

Configuration used: Organization 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 d3511ea and 4ab0446.

📒 Files selected for processing (3)
  • .changeset/dry-pumpkins-design.md
  • apps/meteor/app/api/server/v1/rooms.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • apps/meteor/app/file-upload/server/lib/FileUpload.ts
  • apps/meteor/app/api/server/v1/rooms.ts
🧠 Learnings (5)
📚 Learning: 2025-10-28T16:53:42.761Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37205
File: ee/packages/federation-matrix/src/FederationMatrix.ts:296-301
Timestamp: 2025-10-28T16:53:42.761Z
Learning: In the Rocket.Chat federation-matrix integration (ee/packages/federation-matrix/), the createRoom method from rocket.chat/federation-sdk will support a 4-argument signature (userId, roomName, visibility, displayName) in newer versions. Code using this 4-argument call is forward-compatible with planned library updates and should not be flagged as an error.

Applied to files:

  • apps/meteor/app/api/server/v1/rooms.ts
📚 Learning: 2025-09-19T15:15:04.642Z
Learnt from: rodrigok
Repo: RocketChat/Rocket.Chat PR: 36991
File: apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts:219-221
Timestamp: 2025-09-19T15:15:04.642Z
Learning: The Federation_Matrix_homeserver_domain setting in apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts is part of the old federation system and is being deprecated/removed, so configuration issues with this setting should not be flagged for improvement.

Applied to files:

  • apps/meteor/app/api/server/v1/rooms.ts
📚 Learning: 2025-11-27T17:56:26.050Z
Learnt from: MartinSchoeler
Repo: RocketChat/Rocket.Chat PR: 37557
File: apps/meteor/client/views/admin/ABAC/AdminABACRooms.tsx:115-116
Timestamp: 2025-11-27T17:56:26.050Z
Learning: In Rocket.Chat, the GET /v1/abac/rooms endpoint (implemented in ee/packages/abac/src/index.ts) only returns rooms where abacAttributes exists and is not an empty array (query: { abacAttributes: { $exists: true, $ne: [] } }). Therefore, in components consuming this endpoint (like AdminABACRooms.tsx), room.abacAttributes is guaranteed to be defined for all returned rooms, and optional chaining before calling array methods like .join() is sufficient without additional null coalescing.

Applied to files:

  • apps/meteor/app/api/server/v1/rooms.ts
📚 Learning: 2025-11-04T16:49:19.107Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37377
File: apps/meteor/ee/server/hooks/federation/index.ts:86-88
Timestamp: 2025-11-04T16:49:19.107Z
Learning: In Rocket.Chat's federation system (apps/meteor/ee/server/hooks/federation/), permission checks follow two distinct patterns: (1) User-initiated federation actions (creating rooms, adding users to federated rooms, joining from invites) should throw MeteorError to inform users they lack 'access-federation' permission. (2) Remote server-initiated federation events should silently skip/ignore when users lack permission. The beforeAddUserToRoom hook only executes for local user-initiated actions, so throwing an error there is correct. Remote federation events are handled separately by the federation Matrix package with silent skipping logic.

Applied to files:

  • apps/meteor/app/api/server/v1/rooms.ts
📚 Learning: 2025-11-05T21:04:35.787Z
Learnt from: sampaiodiego
Repo: RocketChat/Rocket.Chat PR: 37357
File: ee/packages/federation-matrix/src/setup.ts:103-120
Timestamp: 2025-11-05T21:04:35.787Z
Learning: In Rocket.Chat's federation-matrix setup (ee/packages/federation-matrix/src/setup.ts and apps/meteor/ee/server/startup/federation.ts), configureFederationMatrixSettings does not need to be called before setupFederationMatrix. The SDK's init() establishes infrastructure (database, event handlers, APIs) first, and the configuration can be applied later via settings watchers before actual federation events are processed. The config only matters when events actually occur, at which point all infrastructure is already configured.

Applied to files:

  • apps/meteor/app/api/server/v1/rooms.ts
🧬 Code graph analysis (1)
apps/meteor/app/file-upload/server/lib/FileUpload.ts (1)
packages/core-services/src/index.ts (1)
  • Media (163-163)
⏰ 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: cubic · AI code reviewer
  • GitHub Check: CodeQL-Build
  • GitHub Check: CodeQL-Build
🔇 Additional comments (3)
apps/meteor/app/api/server/v1/rooms.ts (2)

1-1: LGTM - Import cleanup aligns with centralized EXIF handling.

Removing the Media import is correct since EXIF stripping for attachments has been relocated to FileUpload.ts. This simplifies the dependency footprint in the rooms API module.


211-211: LGTM - Correct use of const for immutable reference.

Changing to const is appropriate since fileBuffer is not reassigned within the function scope (it's only read at lines 231 and 240).

apps/meteor/app/file-upload/server/lib/FileUpload.ts (1)

13-13: Media.stripExifFromBuffer method exists with correct signature and is properly used.

The method is correctly defined in IMediaService as stripExifFromBuffer(buffer: Buffer): Promise<Buffer>, implemented in the image service, and properly imported and awaited at line 412 in FileUpload.ts. No issues to address.

KevLehman
KevLehman previously approved these changes Jan 6, 2026
Copy link
Member

@KevLehman KevLehman left a comment

Choose a reason for hiding this comment

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

https://tenor.com/bYcHP.gif

(would love to have a way to test this, but maybe we have no automated way)

@codecov
Copy link

codecov bot commented Jan 6, 2026

Codecov Report

❌ Patch coverage is 5.00000% with 19 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.65%. Comparing base (c42bb36) to head (4de70cd).
⚠️ Report is 88 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop   #38082      +/-   ##
===========================================
- Coverage    70.66%   70.65%   -0.01%     
===========================================
  Files         3145     3147       +2     
  Lines       108772   108921     +149     
  Branches     19577    19612      +35     
===========================================
+ Hits         76862    76961      +99     
- Misses       29907    29965      +58     
+ Partials      2003     1995       -8     
Flag Coverage Δ
unit 71.85% <5.00%> (+0.07%) ⬆️

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.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 6, 2026

📦 Docker Image Size Report

➡️ Changes

Service Current Baseline Change Percent
sum of all images 0B 0B 0B
account-service 0B 0B 0B
authorization-service 0B 0B 0B
ddp-streamer-service 0B 0B 0B
omnichannel-transcript-service 0B 0B 0B
presence-service 0B 0B 0B
queue-worker-service 0B 0B 0B
rocketchat 0B 0B 0B

📊 Historical Trend

---
config:
  theme: "dark"
  xyChart:
    width: 900
    height: 400
---
xychart
  title "Image Size Evolution by Service (Last 30 Days + This PR)"
  x-axis ["11/18 22:53", "11/19 23:02", "11/21 16:49", "11/24 17:34", "11/27 22:32", "11/28 19:05", "12/01 23:01", "12/02 21:57", "12/03 21:00", "12/04 18:17", "12/05 21:56", "12/08 20:15", "12/09 22:17", "12/10 23:26", "12/11 21:56", "12/12 22:45", "12/13 01:34", "12/15 22:31", "12/16 22:18", "12/17 21:04", "12/18 23:12", "12/19 23:27", "12/20 21:03", "12/22 18:54", "12/23 16:16", "12/24 19:38", "12/25 17:51", "12/26 13:18", "12/29 19:01", "12/30 20:52", "01/15 12:12 (PR)"]
  y-axis "Size (GB)" 0 --> 0.5
  line "account-service" [0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.00]
  line "authorization-service" [0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.00]
  line "ddp-streamer-service" [0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.00]
  line "omnichannel-transcript-service" [0.14, 0.14, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.00]
  line "presence-service" [0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.00]
  line "queue-worker-service" [0.14, 0.14, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.00]
  line "rocketchat" [0.35, 0.35, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.00]
Loading

Statistics (last 30 days):

  • 📊 Average: 1.5GiB
  • ⬇️ Minimum: 1.4GiB
  • ⬆️ Maximum: 1.6GiB
  • 🎯 Current PR: 0B
ℹ️ About this report

This report compares Docker image sizes from this build against the develop baseline.

  • Tag: pr-38082
  • Baseline: develop
  • Timestamp: 2026-01-15 12:12:52 UTC
  • Historical data points: 30

Updated: Thu, 15 Jan 2026 12:12:53 GMT

@alfredodelfabro alfredodelfabro added this to the 8.1.0 milestone Jan 7, 2026
Comment on lines 411 to 414
const fileBuffer = fs.readFileSync(tmpFile);
const strippedFileBuffer = await Media.stripExifFromBuffer(fileBuffer);
fs.writeFileSync(tmpFile, strippedFileBuffer);
}
Copy link
Member

Choose a reason for hiding this comment

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

Please do not use the sync versions of the FS methods, especially when writing a potentially large amount of data, as it blocks the event loop.

Also, here the server will end up buffering the file contents 2 additional times: one reading the file, and another one with the MediaService call. You could unify this by calling Media.stripExifFromStream, passing a read stream from fs.createReadStream - this would prevent any buffering from happening

Copy link
Member

Choose a reason for hiding this comment

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

Aside from that, sharp already removes exif metadata when rotating the image, so we don't need to call the media service in those cases

@d-gubert
Copy link
Member

d-gubert commented Jan 8, 2026

PR #38071 is working to reduce memory usage on file uploads, it looks like it might conflict with this implementation

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: 3

🤖 Fix all issues with AI agents
In `@apps/meteor/app/file-upload/server/lib/FileUpload.ts`:
- Around line 399-401: Remove the leftover debug console.log in FileUpload.ts:
locate the line "console.log('rotating image', file._id)" right after the
s.rotate().withMetadata().toFile(...) call and delete it or replace it with a
structured SystemLogger.debug call (e.g., include context like file._id) if this
event should be observable in logs; ensure no raw console.* calls remain in the
FileUpload class/method.
- Line 872: Remove the debug console.trace('insert') call in FileUpload.ts (the
stray statement in the FileUpload insert flow) to stop printing stack traces on
every insert; replace it with a proper logging call if needed (e.g., use the
existing processLogger or server-side logger at debug/trace level) or simply
delete the line from the insert handler/function to avoid polluting server logs.
- Around line 410-441: Replace the debug console.log and cleanup commented-out
snippets, add proper transformer error handling, and guard unlink so it won't
throw if the temp file was never created: specifically, in the EXIF strip block
that uses tmpFile, exifTmpPath, ExifTransformer, readStream and writeStream,
remove the commented-out pipeline code, replace console.log('ERROR', err) with
SystemLogger.error(`Error stripping exif from image: ${err}`) (or extend the
existing SystemLogger call), attach an 'error' listener to exifTransformer that
rejects the Promise like the other streams, and wrap the await
unlink(exifTmpPath) in a safe check or try/catch (or check fs.exists before
unlink) so unlink won't throw when exifTmpPath was never created.
♻️ Duplicate comments (1)
apps/meteor/app/file-upload/server/lib/FileUpload.ts (1)

443-443: Fix incorrect await on synchronous lstatSync.

fs.lstatSync is synchronous—await has no effect here. Use the async version for consistency with the rest of the file.

-	const { size } = await fs.lstatSync(tmpFile);
+	const { size } = await fs.promises.lstat(tmpFile);
🧹 Nitpick comments (1)
apps/meteor/app/file-upload/server/lib/FileUpload.ts (1)

8-8: Remove unused import.

The pipeline import is not used anywhere in the file—the code at line 433 that would have used it is commented out.

-import { pipeline } from 'stream/promises';
📜 Review details

Configuration used: Organization 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 4ab0446 and 4de70cd.

📒 Files selected for processing (1)
  • apps/meteor/app/file-upload/server/lib/FileUpload.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • apps/meteor/app/file-upload/server/lib/FileUpload.ts
⏰ 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: cubic · AI code reviewer
  • GitHub Check: CodeQL-Build
  • GitHub Check: CodeQL-Build

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +399 to +401
// By default, the metadata is not preserved when rotating the image.
await s.rotate().withMetadata().toFile(`${tmpFile}.tmp`);
console.log('rotating image', file._id);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove debug console.log statement.

Debug logging left in production code. Either remove it or use SystemLogger.debug() if this logging is intentional for observability.

 		// By default, the metadata is not preserved when rotating the image.
 		await s.rotate().withMetadata().toFile(`${tmpFile}.tmp`);
-		console.log('rotating image', file._id);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// By default, the metadata is not preserved when rotating the image.
await s.rotate().withMetadata().toFile(`${tmpFile}.tmp`);
console.log('rotating image', file._id);
// By default, the metadata is not preserved when rotating the image.
await s.rotate().withMetadata().toFile(`${tmpFile}.tmp`);
🤖 Prompt for AI Agents
In `@apps/meteor/app/file-upload/server/lib/FileUpload.ts` around lines 399 - 401,
Remove the leftover debug console.log in FileUpload.ts: locate the line
"console.log('rotating image', file._id)" right after the
s.rotate().withMetadata().toFile(...) call and delete it or replace it with a
structured SystemLogger.debug call (e.g., include context like file._id) if this
event should be observable in logs; ensure no raw console.* calls remain in the
FileUpload class/method.

Comment on lines +410 to +441
const stripExif = settings.get('Message_Attachments_Strip_Exif');

if (stripExif) {
const exifTmpPath = `${tmpFile}.exif-stripped`;

try {
await new Promise<void>((resolve, reject) => {
const readStream = fs.createReadStream(tmpFile);
const writeStream = fs.createWriteStream(exifTmpPath);
const exifTransformer = new ExifTransformer();

readStream.pipe(exifTransformer).pipe(writeStream);
writeStream.on('finish', () => resolve());
readStream.on('error', reject);
writeStream.on('error', reject);
});
// No need to check mime. Library will ignore any files without exif/xmp tags (like BMP, ico, PDF, etc)
// const exifTransformer = new ExifTransformer();
// const readStream = fs.createReadStream(tmpFile);
// const writeStream = fs.createWriteStream(exifTmpPath);

// readStream.pipe(exifTransformer).pipe(writeStream);

// await pipeline(fs.createReadStream(tmpFile), exifTransformer, fs.createWriteStream(exifTmpPath));

await rename(exifTmpPath, tmpFile);
} catch (err) {
console.log('ERROR', err);
await unlink(exifTmpPath);
SystemLogger.error(`Error stripping exif from image: ${err}`);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Clean up debug artifacts and add missing error handling.

Several issues in the EXIF stripping logic:

  1. Line 437: Replace console.log('ERROR', err) with proper logging—it's redundant with line 439 anyway.
  2. Lines 426-433: Remove commented-out code.
  3. Line 419: Missing error handler for exifTransformer stream—if the transformer emits an error, the promise won't reject.
  4. Line 438: unlink(exifTmpPath) may throw if the file was never created (e.g., error occurred during createReadStream).
🛠️ Proposed fix
 	if (stripExif) {
 		const exifTmpPath = `${tmpFile}.exif-stripped`;

 		try {
 			await new Promise<void>((resolve, reject) => {
 				const readStream = fs.createReadStream(tmpFile);
 				const writeStream = fs.createWriteStream(exifTmpPath);
 				const exifTransformer = new ExifTransformer();

 				readStream.pipe(exifTransformer).pipe(writeStream);
 				writeStream.on('finish', () => resolve());
 				readStream.on('error', reject);
+				exifTransformer.on('error', reject);
 				writeStream.on('error', reject);
 			});
-			// No need to check mime. Library will ignore any files without exif/xmp tags (like BMP, ico, PDF, etc)
-			// const exifTransformer = new ExifTransformer();
-			// const readStream = fs.createReadStream(tmpFile);
-			// const writeStream = fs.createWriteStream(exifTmpPath);
-
-			// readStream.pipe(exifTransformer).pipe(writeStream);
-
-			// await pipeline(fs.createReadStream(tmpFile), exifTransformer, fs.createWriteStream(exifTmpPath));

 			await rename(exifTmpPath, tmpFile);
 		} catch (err) {
-			console.log('ERROR', err);
-			await unlink(exifTmpPath);
+			await unlink(exifTmpPath).catch(() => {});
 			SystemLogger.error(`Error stripping exif from image: ${err}`);
 		}
 	}
🤖 Prompt for AI Agents
In `@apps/meteor/app/file-upload/server/lib/FileUpload.ts` around lines 410 - 441,
Replace the debug console.log and cleanup commented-out snippets, add proper
transformer error handling, and guard unlink so it won't throw if the temp file
was never created: specifically, in the EXIF strip block that uses tmpFile,
exifTmpPath, ExifTransformer, readStream and writeStream, remove the
commented-out pipeline code, replace console.log('ERROR', err) with
SystemLogger.error(`Error stripping exif from image: ${err}`) (or extend the
existing SystemLogger call), attach an 'error' listener to exifTransformer that
rejects the Promise like the other streams, and wrap the await
unlink(exifTmpPath) in a safe check or try/catch (or check fs.exists before
unlink) so unlink won't throw when exifTmpPath was never created.

await filter.check(fileData, streamOrBuffer);
}

console.trace('insert');
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Remove debug console.trace statement.

This debug statement will print a stack trace to the console on every file insert operation, polluting server logs.

-		console.trace('insert');
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.trace('insert');
🤖 Prompt for AI Agents
In `@apps/meteor/app/file-upload/server/lib/FileUpload.ts` at line 872, Remove the
debug console.trace('insert') call in FileUpload.ts (the stray statement in the
FileUpload insert flow) to stop printing stack traces on every insert; replace
it with a proper logging call if needed (e.g., use the existing processLogger or
server-side logger at debug/trace level) or simply delete the line from the
insert handler/function to avoid polluting server logs.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

3 issues found across 1 file (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="apps/meteor/app/file-upload/server/lib/FileUpload.ts">

<violation number="1" location="apps/meteor/app/file-upload/server/lib/FileUpload.ts:401">
P2: Debug `console.log` statement should be removed before merging to production. Use `SystemLogger` if logging is needed.</violation>

<violation number="2" location="apps/meteor/app/file-upload/server/lib/FileUpload.ts:419">
P2: Missing error handler for `exifTransformer` stream. If the transformer encounters an error, it won't be caught. Add `exifTransformer.on('error', reject);` to properly handle transformer errors.</violation>

<violation number="3" location="apps/meteor/app/file-upload/server/lib/FileUpload.ts:872">
P2: Debug `console.trace` statement should be removed before merging to production.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

await filter.check(fileData, streamOrBuffer);
}

console.trace('insert');
Copy link
Contributor

Choose a reason for hiding this comment

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

P2: Debug console.trace statement should be removed before merging to production.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/app/file-upload/server/lib/FileUpload.ts, line 872:

<comment>Debug `console.trace` statement should be removed before merging to production.</comment>

<file context>
@@ -842,7 +869,7 @@ export class FileUploadClass {
 			await filter.check(fileData, streamOrBuffer);
 		}
-
+		console.trace('insert');
 		return this._doInsert(fileData, streamOrBuffer, { session: options?.session });
 	}
</file context>


// By default, the metadata is not preserved when rotating the image.
await s.rotate().withMetadata().toFile(`${tmpFile}.tmp`);
console.log('rotating image', file._id);
Copy link
Contributor

Choose a reason for hiding this comment

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

P2: Debug console.log statement should be removed before merging to production. Use SystemLogger if logging is needed.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/app/file-upload/server/lib/FileUpload.ts, line 401:

<comment>Debug `console.log` statement should be removed before merging to production. Use `SystemLogger` if logging is needed.</comment>

<file context>
@@ -395,8 +396,9 @@ export const FileUpload = {
-
+			// By default, the metadata is not preserved when rotating the image.
+			await s.rotate().withMetadata().toFile(`${tmpFile}.tmp`);
+			console.log('rotating image', file._id);
 			await unlink(tmpFile);
 
</file context>

Comment on lines +419 to +424
const exifTransformer = new ExifTransformer();

readStream.pipe(exifTransformer).pipe(writeStream);
writeStream.on('finish', () => resolve());
readStream.on('error', reject);
writeStream.on('error', reject);
Copy link
Contributor

Choose a reason for hiding this comment

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

P2: Missing error handler for exifTransformer stream. If the transformer encounters an error, it won't be caught. Add exifTransformer.on('error', reject); to properly handle transformer errors.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/app/file-upload/server/lib/FileUpload.ts, line 419:

<comment>Missing error handler for `exifTransformer` stream. If the transformer encounters an error, it won't be caught. Add `exifTransformer.on('error', reject);` to properly handle transformer errors.</comment>

<file context>
@@ -408,9 +410,34 @@ export const FileUpload = {
+				await new Promise<void>((resolve, reject) => {
+					const readStream = fs.createReadStream(tmpFile);
+					const writeStream = fs.createWriteStream(exifTmpPath);
+					const exifTransformer = new ExifTransformer();
+
+					readStream.pipe(exifTransformer).pipe(writeStream);
</file context>
Suggested change
const exifTransformer = new ExifTransformer();
readStream.pipe(exifTransformer).pipe(writeStream);
writeStream.on('finish', () => resolve());
readStream.on('error', reject);
writeStream.on('error', reject);
const exifTransformer = new ExifTransformer();
readStream.pipe(exifTransformer).pipe(writeStream);
writeStream.on('finish', () => resolve());
readStream.on('error', reject);
writeStream.on('error', reject);
exifTransformer.on('error', reject);

@alfredodelfabro alfredodelfabro modified the milestones: 8.1.0, 8.2.0 Jan 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants