Skip to content

fix: sanitize Content-Disposition attachment filenames - #788

Merged
imuniqueshiv merged 5 commits into
imuniqueshiv:mainfrom
Ayush-0918:fix/sanitize-content-disposition-786
Jul 29, 2026
Merged

imuniqueshiv merged 5 commits into
imuniqueshiv:mainfrom
Ayush-0918:fix/sanitize-content-disposition-786

Conversation

@Ayush-0918

@Ayush-0918 Ayush-0918 commented Jul 28, 2026 •

Copy link
Copy Markdown
Contributor

Pull Request

Description

This PR improves the security of attachment downloads by sanitizing filenames before they are included in the Content-Disposition response header. Unsafe characters that could lead to malformed headers or header injection are handled safely, while valid filenames continue to work as expected without changing the existing download behavior.

Type of Change

  • New Feature
  • Bug Fix
  • Documentation Update
  • Refactor
  • Performance Improvement
  • Security Improvement
  • Other

Related Issue

Testing

The following scenarios were verified:

  • Tested locally
  • Existing functionality verified
  • No new warnings or errors

Additional testing performed:

  • Verified valid filenames are preserved during downloads.
  • Confirmed filenames containing unsafe characters are sanitized before being added to the Content-Disposition header.
  • Verified malformed or header injection payloads cannot alter response headers.
  • Ensured existing attachment download functionality remains unchanged.

Screenshots

N/A

Checklist

  • Code follows project conventions
  • Documentation updated where required
  • No unnecessary files included
  • Changes have been tested
  • Ready for review

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **Bug Fixes**
  * Improved the safety and consistency of downloaded filenames for meeting, transcript, and graph snapshot exports.
  * Ensured attachment names are sanitized to prevent malformed headers and header-injection via control characters and quotes.
  * Preserved allowed filename characters and non-ASCII text in downloads.
* **Tests**
  * Added/expanded automated coverage for filename sanitization and correct Content-Disposition header generation, including Unicode and injection attempts.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

@vercel

vercel Bot commented Jul 28, 2026

Copy link
Copy Markdown

@Ayush-0918 is attempting to deploy a commit to the Shiv Raj Singh's projects Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions github-actions Bot added the ECSoC26 Eligible for Elite Coders Summer of Code 2026 automated scoring. label Jul 28, 2026
@github-actions

github-actions Bot commented Jul 28, 2026 •

Copy link
Copy Markdown
Contributor

Hi @Ayush-0918, thank you so much for your PR! 🎉

📋 PR Review Checklist

We appreciate the effort you've put in — let us know if you need any help getting this across the finish line! 💪

@coderabbitai

coderabbitai Bot commented Jul 28, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@Ayush-0918, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 29 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: eb06bc78-c3ff-4d39-9d90-7c7eea8f1c0d

📥 Commits

Reviewing files that changed from the base of the PR and between b0cdace and 556b6e8.

⛔ Files ignored due to path filters (1)
  • server/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (4)
  • scripts/validation/changed-files.mjs
  • server/controllers/exportController.js
  • server/controllers/transcriptController.js
  • server/eslint-output.json
📝 Walkthrough

Walkthrough

Shared filename-sanitization helpers were added and tested, then integrated into meeting, graph snapshot, and transcript export controllers to sanitize Content-Disposition attachment filenames.

Changes

Filename Sanitization

Layer / File(s) Summary
Sanitizer contract and validation
server/utils/fileUtils.js, server/tests/fileUtils.test.js
Adds filename sanitization and RFC 8187 Content-Disposition helpers with coverage for unsafe characters, Unicode, encoding, injection attempts, and edge cases.
Export filename integration
server/controllers/exportController.js, server/controllers/graphSnapshotController.js, server/controllers/transcriptController.js
Applies the helpers to meeting, snapshot, and transcript attachment filenames.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related issues

  • imuniqueshiv/MeetOnMemory#807 — Addresses the same filename-sanitization objective for dynamically generated Content-Disposition filenames in another download controller.
🚥 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 clearly summarizes the main change: sanitizing attachment filenames in Content-Disposition headers.
Linked Issues check ✅ Passed The changes sanitize header filenames, preserve valid downloads, and keep attachment behavior intact as requested in #786.
Out of Scope Changes check ✅ Passed The added utility, controller updates, and tests are all directly related to the filename sanitization objective.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot 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: 4

🤖 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 `@server/controllers/transcriptController.js`:
- Around line 734-735: In the setHeader call around sanitizeFilenameForHeader,
remove the stale unsanitized transcript filename literal and ensure the
sanitized filename argument is properly terminated with a comma so the module
parses correctly.

In `@server/tests/fileUtils.test.js`:
- Around line 3-112: Run Prettier on the sanitizeFilenameForHeader test suite in
this file and commit the formatter’s output, including spacing, line wrapping,
and whitespace normalization; do not alter the test behavior or assertions.

In `@server/utils/fileUtils.js`:
- Around line 36-37: Update the filename sanitization chain in fileUtils to
remove only the quote character used to delimit the caller’s filename value,
while preserving apostrophes within names such as “Bob’s Notes.pdf”; narrow the
existing regex in the quote-removal step without changing other filename
processing.
- Around line 31-39: Update sanitizeFilenameForHeader and the
Content-Disposition response-header construction to support non-Latin-1
filenames: generate an ASCII-safe fallback for filename and add
filename*=UTF-8'' with the UTF-8 percent-encoded original name per RFC 8187.
Preserve sanitization of control characters and quotes, and add coverage for
Unicode filenames in the response header path.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 87fc0d14-a639-406c-a3f1-e29683ca90b7

📥 Commits

Reviewing files that changed from the base of the PR and between 98fe8cb and e91252f.

⛔ Files ignored due to path filters (1)
  • server/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (5)
  • server/controllers/exportController.js
  • server/controllers/graphSnapshotController.js
  • server/controllers/transcriptController.js
  • server/tests/fileUtils.test.js
  • server/utils/fileUtils.js

Comment thread server/controllers/transcriptController.js Outdated
Comment thread server/tests/fileUtils.test.js
Comment thread server/utils/fileUtils.js Outdated
Comment thread server/utils/fileUtils.js Outdated
@imuniqueshiv

Copy link
Copy Markdown
Owner

@Ayush-0918 Could you please clear all the checks?

- Fixed malformed setHeader() call in transcriptController.js
- Implemented RFC 8187 compliant Content-Disposition headers for Unicode filenames
- Preserved single quotes in fileUtils.js sanitization logic
- Updated fileUtils.test.js with coverage for Unicode response headers
- Formatted modified files with Prettier

@coderabbitai coderabbitai Bot 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)
server/tests/fileUtils.test.js (1)

136-154: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add coverage for the composite getContentDispositionHeader security guarantees.

This suite only exercises 3 happy-path cases. Given this function is the actual thing shipped into the response header, it's worth directly testing the properties this PR is meant to guarantee, none of which are currently covered end-to-end:

  • A CRLF/header-injection payload (e.g. "file.pdf\r\nX-Injected: x") round-tripped through getContentDispositionHeader itself, not just sanitizeFilenameForHeader.
  • null/undefined input — encodeURIComponent(null) coerces to the literal string "null", so filename*=UTF-8''null would currently be produced.
  • A filename containing ', (, ), or * in the filename* segment. encodeURIComponent intentionally leaves these characters unescaped, but RFC 5987/8187 attr-char excludes them from the extended-value charset, so getContentDispositionHeader's use of plain encodeURIComponent for encoded is not fully spec-compliant for these characters.
Suggested additional test cases
   it("should sanitize the fallback ascii filename", () => {
     expect(getContentDispositionHeader('bad"name.pdf')).toBe(
       `attachment; filename="badname.pdf"; filename*=UTF-8''bad%22name.pdf`,
     );
   });
+
+  it("should not allow CRLF injection through the composite header", () => {
+    const header = getContentDispositionHeader(
+      "file.pdf\r\nX-Injected-Header: malicious",
+    );
+    expect(header).not.toMatch(/[\r\n]/);
+  });
+
+  it("should handle null/undefined without emitting the literal string", () => {
+    expect(getContentDispositionHeader(null)).not.toContain("UTF-8''null");
+  });
});

As per the review-stack context, server/utils/fileUtils.js is part of this same layer ("Sanitizer contract and validation"); confirm whether encodeURIComponent's unescaped ' ( ) * characters and encodeURIComponent(null) producing "null" are acceptable for this PR's scope, since MDN's own RFC 5987 reference implementation explicitly patches for this gap.

🤖 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 `@server/tests/fileUtils.test.js` around lines 136 - 154, Add end-to-end tests
for getContentDispositionHeader covering CRLF/header-injection input, null and
undefined values, and filenames containing apostrophe, parentheses, or asterisk
characters. Verify the complete header output, including sanitized fallback and
RFC 5987/8187-compliant filename* encoding; update the implementation used by
getContentDispositionHeader if these cases currently produce unsafe or
non-compliant output.
🤖 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 `@server/tests/fileUtils.test.js`:
- Around line 143-147: Add a CJK or emoji case to the
getContentDispositionHeader tests and verify the filename= fallback remains
ASCII while filename* retains the correctly encoded original name. Update
sanitizeFilenameForHeader or the surrounding getContentDispositionHeader logic
to ASCII-normalize or replace non-Latin-1 characters when necessary, while
preserving existing sanitization and Latin-1 behavior.

---

Nitpick comments:
In `@server/tests/fileUtils.test.js`:
- Around line 136-154: Add end-to-end tests for getContentDispositionHeader
covering CRLF/header-injection input, null and undefined values, and filenames
containing apostrophe, parentheses, or asterisk characters. Verify the complete
header output, including sanitized fallback and RFC 5987/8187-compliant
filename* encoding; update the implementation used by
getContentDispositionHeader if these cases currently produce unsafe or
non-compliant output.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8cd9f6c0-747d-415a-b6dc-e0b61c7728ac

📥 Commits

Reviewing files that changed from the base of the PR and between e91252f and b0cdace.

📒 Files selected for processing (3)
  • server/controllers/transcriptController.js
  • server/tests/fileUtils.test.js
  • server/utils/fileUtils.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • server/utils/fileUtils.js
  • server/controllers/transcriptController.js

Comment thread server/tests/fileUtils.test.js
@vercel

vercel Bot commented Jul 29, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
meetonmemory Ready Ready Preview, Comment Jul 29, 2026 2:01pm

@imuniqueshiv
imuniqueshiv merged commit f32df31 into imuniqueshiv:main Jul 29, 2026
8 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Hi @Ayush-0918, thank you so much for this solid contribution! 🎉🚀

Your PR #788 (fix: sanitize Content-Disposition attachment filenames) has been merged. ✅

Related issue: #786 (Sanitize Attachment Content-Disposition Filename).

This update fits nicely into the project and is genuinely appreciated — great work! We'd love to see you contribute again soon. 💙

⭐ If you're enjoying MeetOnMemory, please consider starring the repository — it really helps us out!

This branch was successfully deployed

1 active deployment
Preview — 556b6e8a Deployed Jul 29, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ECSoC26-L3 ECSoC26 Eligible for Elite Coders Summer of Code 2026 automated scoring. good-backend +50 XP

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Sanitize Attachment Content-Disposition Filename

2 participants