Thanks to visit codestin.com
Credit goes to github.com

Skip to content

🐛 bug: sanitize attachment/download filenames#4070

Merged
ReneWerner87 merged 8 commits intomainfrom
normalize-filename-in-attachment-and-download
Feb 11, 2026
Merged

🐛 bug: sanitize attachment/download filenames#4070
ReneWerner87 merged 8 commits intomainfrom
normalize-filename-in-attachment-and-download

Conversation

@gaby
Copy link
Member

@gaby gaby commented Feb 9, 2026

Motivation

  • Prevent control characters (CR/LF, TAB, NUL) from appearing in the Content-Disposition filename parameter to avoid header injection and unsafe/ambiguous client dialogs.
  • Ensure a safe, non-empty default filename is used when sanitization removes all characters.
  • Expand coverage for attachment/download behavior to verify handling of spaces, nested paths, and control characters.

Description

  • Added sanitizeFilename in res.go which strips \r, \n, \t, and NUL using strings.Map and applied it to DefaultRes.Attachment and DefaultRes.Download with a fallback to "download" when the sanitized name is empty.
  • Adjusted Attachment/Download logic to sanitize the base filename before determining Content-Type and building the Content-Disposition header, while preserving ASCII vs. non-ASCII quoting and filename* behavior.
  • Regenerated small doc comment changes in ctx_interface_gen.go (minor interface comment updates) to match current generation output.
  • Expanded tests in ctx_test.go: added cases to Test_Ctx_Attachment (spaces and nested paths), extended Test_Ctx_Attachment_SanitizesFilenameControls (additional control-placement cases), and added Test_Ctx_Download_SanitizesFilenameControls to assert sanitized headers and absence of raw control bytes.

@gaby gaby requested a review from a team as a code owner February 9, 2026 05:30
Copilot AI review requested due to automatic review settings February 9, 2026 05:30
@gaby gaby requested review from ReneWerner87, efectn and sixcolors and removed request for Copilot February 9, 2026 05:30
@ReneWerner87 ReneWerner87 added this to v3 Feb 9, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @gaby, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the security and robustness of file serving by implementing comprehensive filename sanitization for Content-Disposition headers in both attachment and download operations. The changes prevent potential header injection vulnerabilities by stripping control characters from filenames and ensure a safe, non-empty default filename is used when necessary. This is complemented by expanded test coverage to validate the new sanitization logic across various filename scenarios.

Highlights

  • Filename Sanitization: Implemented robust filename sanitization for Content-Disposition headers in Attachment and Download methods to prevent header injection by stripping control characters (CR/LF, TAB, NUL).
  • Default Filename Fallback: Introduced a fallback mechanism to use "download" as a default filename if the sanitization process results in an empty string, ensuring a valid filename is always provided.
  • Expanded Test Coverage: Significantly expanded unit test coverage for Attachment and Download methods, including new test cases for filenames with spaces, nested paths, and various placements of control characters to validate the sanitization logic.
  • New Utility Function: Added a new sanitizeFilename utility function in res.go responsible for removing unwanted control characters from filenames.
  • Documentation Updates: Updated documentation comments in ctx_interface_gen.go for Request, Response, and Value methods to clarify their behavior when the context has been released.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • ctx_interface_gen.go
    • Updated doc comments for Request(), Response(), and Value() methods to clarify behavior when the context is released.
  • ctx_test.go
    • Enhanced Test_Ctx_Attachment with new test cases for filenames containing spaces and nested paths.
    • Added Test_Ctx_Attachment_SanitizesFilenameControls to specifically test the stripping of control characters from attachment filenames and the "download" fallback.
    • Added Test_Ctx_Download_SanitizesFilenameControls to verify control character stripping for download filenames and the "download" fallback.
    • Adjusted Benchmark_Ctx_Attachment to align with the new sanitization logic.
  • res.go
    • Introduced a new sanitizeFilename function to remove carriage return (\r), newline (\n), tab (\t), and null (\x00) characters from strings.
    • Integrated sanitizeFilename into DefaultRes.Attachment and DefaultRes.Download methods, applying it to the base filename and providing a "download" fallback for empty results.
Activity
  • The make audit command was executed and reported vulnerabilities in the Go standard library.
  • Code generation, formatting, and linting tools (make generate, make betteralign, make modernize, make format, make lint) were run successfully.
  • All unit tests, including the newly added ones for filename sanitization, passed successfully.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@ReneWerner87 ReneWerner87 added this to the v3 milestone Feb 9, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 9, 2026

Walkthrough

Adds internal filename sanitization and fallback for response Attachment/Download headers; expands inline context comments to note Request(), Response(), and Value(key any) may return nil after context release; tests and benchmarks updated to reflect sanitized filename behavior.

Changes

Cohort / File(s) Summary
Context docs
ctx_interface_gen.go
Expanded inline comments for Request(), Response(), and Value(key any) to state they may return nil if the context has been released.
Filename sanitization & fallback
res.go
Added sanitizeFilename(filename string) string to strip control runes (CR, LF, tab, NUL) and trim results, plus fallbackFilenameIfInvalid(filename string) to return a default "download" for invalid names; applied in Attachment and Download header construction and content-type resolution.
Tests & benchmarks
ctx_test.go
Added and updated tests verifying filename sanitization (spaces, nested paths, quoting, control characters) for Attachment and Download, and adjusted benchmark expectations to match sanitized encodings.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • sixcolors
  • efectn

Poem

🐰 I nibble CR, LF, tabs, and NUL with care,
Trim paths and spaces so filenames are fair.
Downloads arrive tidy, headers neat and sound,
A little rabbit fixes bytes all around.
Hop, sanitize, deliver — safe files on the ground.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 77.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: filename sanitization for attachment/download functionality to fix a security/safety issue with control characters.
Description check ✅ Passed The description is comprehensive and well-structured, addressing motivation, implementation details, and test coverage. It clearly explains the problem being solved and the approach taken.

✏️ 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 normalize-filename-in-attachment-and-download

No actionable comments were generated in the recent review. 🎉


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.

@codecov
Copy link

codecov bot commented Feb 9, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.07%. Comparing base (f93613f) to head (d224310).
⚠️ Report is 42 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4070      +/-   ##
==========================================
- Coverage   91.08%   91.07%   -0.02%     
==========================================
  Files         119      119              
  Lines       11221    11288      +67     
==========================================
+ Hits        10221    10280      +59     
- Misses        633      637       +4     
- Partials      367      371       +4     
Flag Coverage Δ
unittests 91.07% <100.00%> (-0.02%) ⬇️

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

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces important security hardening by sanitizing filenames used in Content-Disposition headers for attachments and downloads, effectively preventing potential header injection vulnerabilities. The implementation is sound and the added tests provide good coverage for the new logic.

I have a few suggestions to further improve the implementation by making the sanitization more comprehensive and reducing code duplication. These changes would enhance robustness and maintainability.

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
res.go (1)

330-340: ⚠️ Potential issue | 🟠 Major

Download should normalize provided filenames with filepath.Base to match Attachment behavior.

When a caller passes a display filename to Download (e.g., c.Download(file, "../../name.txt")), path separators are preserved in the Content-Disposition header. In contrast, Attachment (line 194) calls filepath.Base on the filename parameter before sanitization, which strips any path components. The sanitizeFilename function only removes control characters (\r, \n, \t, \0), not path separators. This inconsistency should be resolved by applying filepath.Base to the explicitly provided filename in Download, as is done in Attachment.

🧹 Nitpick comments (2)
res.go (2)

180-189: Consider stripping all control characters, not just four specific ones.

The current implementation strips \r, \n, \t, and NUL, which covers header injection. However, other C0 control characters (e.g., 0x010x1F) are also invalid/dangerous in filenames and Content-Disposition values. A broader filter would be more defensive:

♻️ Suggested broader control character filter
 func sanitizeFilename(filename string) string {
 	return strings.Map(func(r rune) rune {
-		switch r {
-		case '\r', '\n', '\t', 0:
+		if r < 0x20 || r == 0x7F {
 			return -1
-		default:
-			return r
 		}
+		return r
 	}, filename)
 }

This would also strip BEL, ESC, backspace, and other characters that have no legitimate use in filenames.


192-215: Duplicated Content-Disposition header construction between Attachment and Download.

Lines 200–211 and 341–352 are nearly identical: ASCII check → quote → build disposition string → optional filename* parameter. Consider extracting a shared helper (e.g., buildContentDisposition(fname string)) to reduce duplication and ensure both paths stay consistent as the logic evolves.

Also applies to: 330-354

Copilot AI review requested due to automatic review settings February 9, 2026 06:04
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 `@res.go`:
- Around line 196-198: The Attachment code uses filepath.Base(filename[0]) then
sanitizeFilename(fname) but doesn't handle cases where Base returns "." or "/"
(or strings of only slashes), causing invalid Content-Disposition filenames;
update the Attachment handling to check the basename returned from filepath.Base
(and the resulting fname) for ".", "/", "" or strings made only of slashes and
replace them with a safe default like "attachment" or "file" before calling
sanitizeFilename (or sanitize after replacement) so sanitizeFilename is never
given "." or "/" as the filename.
🧹 Nitpick comments (1)
res.go (1)

194-215: Consider extracting the shared Content-Disposition construction logic.

Both Attachment (lines 200–211) and Download (lines 338–349) contain identical logic for building the Content-Disposition header (ASCII check → quote → assemble attachment; filename=… → optional filename*=UTF-8''…). Extracting this into a small helper (e.g., buildContentDisposition(app *App, fname string)) would eliminate the duplication and reduce the risk of the two paths diverging in the future.

♻️ Proposed refactor
+func buildContentDisposition(app *App, fname string) string {
+	var quoted string
+	if app.isASCII(fname) {
+		quoted = app.quoteString(fname)
+	} else {
+		quoted = app.quoteRawString(fname)
+	}
+	disp := `attachment; filename="` + quoted + `"`
+	if !app.isASCII(fname) {
+		disp += `; filename*=UTF-8''` + url.PathEscape(fname)
+	}
+	return disp
+}

Then in Attachment:

 		fname = sanitizeFilename(fname)
 		r.Type(filepath.Ext(fname))
-		app := r.c.app
-		var quoted string
-		if app.isASCII(fname) {
-			quoted = app.quoteString(fname)
-		} else {
-			quoted = app.quoteRawString(fname)
-		}
-		disp := `attachment; filename="` + quoted + `"`
-		if !app.isASCII(fname) {
-			disp += `; filename*=UTF-8''` + url.PathEscape(fname)
-		}
-		r.setCanonical(HeaderContentDisposition, disp)
+		r.setCanonical(HeaderContentDisposition, buildContentDisposition(r.c.app, fname))

And similarly in Download.

Also applies to: 330-351

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens Content-Disposition handling for Attachment and Download by stripping control characters from the filename to prevent unsafe header values, and expands tests to validate expected sanitization behavior.

Changes:

  • Added sanitizeFilename and applied it to DefaultRes.Attachment and DefaultRes.Download, with a non-empty fallback name.
  • Updated attachment/download header expectations (notably removing CR/LF/TAB/NUL from encoded output).
  • Expanded ctx_test.go coverage for spaces, nested paths, and multiple control-character placements; regenerated interface doc comments.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
res.go Introduces filename sanitization and applies it to attachment/download Content-Disposition generation.
ctx_test.go Adds/updates tests to assert control-character stripping and some additional filename cases.
ctx_interface_gen.go Regenerates/updates interface comments related to released contexts.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

…tions

no_controls: current ~12 ns vs old strings.Map ~25–28 ns
controls_present: current ~37 ns vs old strings.Map ~55 ns
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
res.go (1)

200-216: ⚠️ Potential issue | 🟠 Major

Missing empty-filename fallback after sanitization.

The PR objectives state a fallback to "download" when the sanitized filename is empty, but neither Attachment nor Download implements this guard. If a caller passes a filename composed entirely of control characters (e.g., "\r\n"), sanitizeFilename returns "", producing Content-Disposition: attachment; filename="" and an empty extension for r.Type().

Additionally, filepath.Base can return "." for inputs like "" or ".", which survives sanitization but is not a meaningful filename.

Proposed fix for Attachment
 	if len(filename) > 0 {
 		fname := filepath.Base(filename[0])
 		fname = sanitizeFilename(fname)
+		if fname == "" || fname == "." {
+			fname = "download"
+		}
 		r.Type(filepath.Ext(fname))
🤖 Fix all issues with AI agents
In `@res.go`:
- Around line 337-341: After calling sanitizeFilename on fname (set from
filename[0] or file), add the same empty-filename fallback used in Attachment:
check if fname == "" or fname == "." and if so assign a sensible default (e.g.,
"download" or "file") so Download does not propagate empty or "." names; update
the block that sets fname (using filename, file, and sanitizeFilename) to
include this conditional fallback.

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.50.

Benchmark suite Current: d224310 Previous: 7b92748 Ratio
Benchmark_Ctx_Attachment - B/op 168 B/op 104 B/op 1.62
Benchmark_Ctx_Attachment - allocs/op 4 allocs/op 2 allocs/op 2

This comment was automatically generated by workflow using github-action-benchmark.

@ReneWerner87
Copy link
Member

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark. Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.50.

Benchmark suite Current: d224310 Previous: 7b92748 Ratio
Benchmark_Ctx_Attachment - B/op 168 B/op 104 B/op 1.62
Benchmark_Ctx_Attachment - allocs/op 4 allocs/op 2 allocs/op 2
This comment was automatically generated by workflow using github-action-benchmark.

yes we know, but its the same speed -> so okay in terms of security / speed

@ReneWerner87 ReneWerner87 merged commit 918fdfd into main Feb 11, 2026
16 of 17 checks passed
@ReneWerner87 ReneWerner87 deleted the normalize-filename-in-attachment-and-download branch February 11, 2026 14:13
@github-project-automation github-project-automation bot moved this to Done in v3 Feb 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants