Skip to content

fix(res.set): remove implicit mime lookup and charset injection for Content-Type #7146

Open
Pandey-Krishnaa wants to merge 1 commit intoexpressjs:masterfrom
Pandey-Krishnaa:fix/res-set-no-mime-lookup
Open

fix(res.set): remove implicit mime lookup and charset injection for Content-Type #7146
Pandey-Krishnaa wants to merge 1 commit intoexpressjs:masterfrom
Pandey-Krishnaa:fix/res-set-no-mime-lookup

Conversation

@Pandey-Krishnaa
Copy link
Copy Markdown

Closes #7034

res.set('Content-Type', value) was silently calling mime.contentType() on the value, which would:

  • Perform a mime-type lookup if the value had no / (e.g. 'html''text/html; charset=utf-8')
  • Append a charset if none was present (e.g. 'text/plain''text/plain; charset=utf-8')

This hidden mutation is unexpected — res.set is a generic header setter and should not transform user input.

Solution

Remove the Content-Type special-casing from res.set/res.header. Users who want mime lookup + charset behaviour
should use res.type(), which already exists for exactly this purpose.

Changes

  • Remove the mime.contentType() block from res.set
  • res.json now explicitly sets 'application/json; charset=utf-8' so its behaviour is unchanged
  • Update tests to reflect the new pass-through semantics of res.set

Breaking Change

res.set('Content-Type', 'text/plain') will no longer silently become text/plain; charset=utf-8. Use
res.type('text/plain') if charset injection is desired.

…ontent-Type

Previously, res.set('Content-Type', value) silently called mime.contentType()
on the value, which would:
  - perform a mime-type lookup if the value contained no '/' (e.g. 'html' → 'text/html; charset=utf-8')
  - append a charset if none was present (e.g. 'text/plain' → 'text/plain; charset=utf-8')

This hidden mutation was unexpected: res.set is a generic header setter and
should not transform user input. res.type() already exists as the dedicated
API for mime-lookup + charset behaviour, and callers that want it should use
that method explicitly.

Changes:
- Remove the Content-Type special-case block from res.set/res.header
- res.json now explicitly sets 'application/json; charset=utf-8' so its
  behaviour is unchanged without relying on the removed magic
- Update tests to reflect the new pass-through semantics of res.set

Fixes: expressjs#7034
@Qodo-Free-For-OSS
Copy link
Copy Markdown

Hi, res.set/res.header no longer rejects array values for Content-Type, allowing this.setHeader('Content-Type', ['...']). When res.send(string) runs, it treats a non-string Content-Type as missing and overwrites it with the default HTML type, silently corrupting the user’s header.

Severity: action required | Category: correctness

How to fix: Reject Content-Type array values

Agent prompt to fix - you can give this to your LLM of choice:

Issue description

res.set/res.header now allow setting Content-Type to an array. This interacts badly with res.send(string), which only treats a string Content-Type as “set”; array values are treated as missing and get overwritten to text/html; charset=utf-8.

Issue Context

Previously Content-Type had special handling that rejected arrays. The PR removed this block along with the mime lookup behavior.

Fix Focus Areas

  • lib/response.js[664-678]
  • lib/response.js[125-145]

What to change

  • Reintroduce an explicit guard for Content-Type when val is an array (e.g., throw TypeError('Content-Type cannot be set to an Array')), while keeping the new behavior of not calling mime.contentType() or injecting charset.
  • Add/restore a test to ensure res.set('Content-Type', ['text/html']) throws (or otherwise does not silently corrupt behavior).

We noticed a couple of other issues in this PR as well - happy to share if helpful.


Spotted by Qodo code review - free for open-source projects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

res.set('Content-Type') silently sets header to literal string 'false' for unknown types

2 participants