-
Notifications
You must be signed in to change notification settings - Fork 188
fix(event-handler): handle set-cookie header values with multiple attributes #4990
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
Thanks a lot for your first contribution! Please check out our contributing guidelines and don't hesitate to ask whatever you need. |
dreamorosi
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| // Handle set-cookie headers specially using getSetCookie() | ||
| const cookies = webHeaders.getSetCookie(); | ||
| // Some legacy implementations may concatenate multiple set-cookie values with commas | ||
| // Split them out while preserving the cookie attributes (which use semicolons) | ||
| const allCookies: string[] = []; | ||
| for (const cookie of cookies) { | ||
| allCookies.push(...cookie.split(',').map((v) => v.trimStart())); | ||
| } | ||
|
|
||
| if (allCookies.length > 1) { | ||
| multiValueHeaders['set-cookie'] = allCookies; | ||
| } else if (allCookies.length === 1) { | ||
| headers['set-cookie'] = allCookies[0]; | ||
| } | ||
|
|
||
| for (const [key, value] of webHeaders.entries()) { | ||
| // Skip set-cookie as it's already handled above |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's remove these comments please
| if (headers[key]) { | ||
| multiValueHeaders[key] = [headers[key], ...values]; | ||
| delete headers[key]; | ||
| } else if (values.length > 1) { | ||
| if (values.length > 1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we removing this logic?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To my knowledge set-cookie is the only header that can have multiple keys. Other headers, when appended to, will append the value to the end via a comma. This logic would never be evaluated (which was verified through the code coverage)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a lot of diffs that seem to be unrelated to this PR - any chance you could revert?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yeah. Looks like my own biome got the best of me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, it looks like these changes were automatically made via lint-staged commit hook. After reverting them and attempting to commit, my revert gets stashed and I'm left with an empty commit which is prevented.
| // Prepare | ||
| const headers = new Headers({ | ||
| 'set-cookie': 'session=abc123; theme=dark', | ||
| 'cache-control': 'no-cache; no-store', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How come we're changing these?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
set-cookie is a header that shouldn't be separated by a semicolon. Keeping it as it were would result in a single set-cookie header with an unsupported 'theme' attribute which would cause the test to fail and ultimately seems to defeat the purpose of the test. cache-control represents a normal header that can be separated by a semicolon. Other tests cover the nuances of 'set-cookie'.
| headers: { 'content-type': 'application/json' }, | ||
| multiValueHeaders: { 'set-cookie': ['session=abc123', 'theme=dark'] }, | ||
| multiValueHeaders: { | ||
| 'cache-control': ['no-cache', 'no-store'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where are these cache-control headers coming from?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added them to illustrate a 'simple' multi-value header pair along with the set-cookie multi-value header which contains a semicolon.
| continue; | ||
| } | ||
|
|
||
| const values = value.split(/[;,]/).map((v) => v.trimStart()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ultimately, splitting on a semicolon seems out of place and is the root of this entire issue. If a goal wasn't to keep backwards compatibility I would suggest removing it and using a comma only. Headers by default a separated by a comma.
const headers = new Headers({
'accept': 'application/json'
})
headers.append('accept', 'text/html');
const acceptHeaders = headers.get('accept'); // Results in "application/json, text/html"There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the reason I wrote it this way was because I was under the impression that cookies were an exception in that they could be separated by a semicolon. I got this from the RFC 6265:
The user agent sends stored cookies to the origin server in the
Cookie header. If the server conforms to the requirements in
Section 4.1 (and the user agent conforms to the requirements in
Section 5), the user agent will send a Cookie header that conforms to
the following grammar:cookie-header = "Cookie:" OWS cookie-string OWS
cookie-string = cookie-pair *( ";" SP cookie-pair )
It looks like I made a mistake and thought this referred to the Set-Cookie header rather than the Cookie header as indicated in the quote above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What this means though is that to handle the Cookie header case correctly we do need to have special handling for the semicolon. Btw, regarding this:
If a goal wasn't to keep backwards compatibility
We don't need to preserve backward compatibility for what was a bug. If we change this so only Cookie gets this handling and that's fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the references to the RFC. In this particular circumstance the webHeadersToApiGatewayV1Headers function is converting from server to user agent (not also user agent to server) so we should be able to omit special handling of the Cookie header as per RFC6265 4.2 the Cookie header is a user agent to server header.
It seems like the semicolon was included to handle the special usage of the 'Cookie' header which we don't actually need here.
We don't need to preserve backward compatibility for what was a bug. If we change this so only Cookie gets this handling and that's fine.
I see your point here; however I think only set-cookie with a semicolon is a bug. Since the current implementation accepts any header with a semicolon separated value (while it may not be popular) removing it might break current implementations.
I'm unsure of the best path here. Ultimately it seems like dropping the semicolon would simplify and unify the usage across the board (all headers are treated the same).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue here is larger though, splitting on ; causes worse problems. The accepted way to inidicate parameters in a header is with a semi colon, e.g., Accept: text/html; q=0.5, text/plain; q=0.2. With the current implementation, this ends up with a multivalue header value which is clearly wrong:
// ...
multiValueHeaders: {
accept: [
"text/html",
"q=0.5",
"text/plain",
"q=0.2",
]
}
// ...It's far more important that we handle this case correctly than headers that aren't Cookie erroneously delimited by ;.
|
My comments were addressed, will leave to @svozza to bring the PR to merge state.



Summary
This fixes the scenario where a single
set-cookieheader valuefoo=bar; Max-Age=3600is treated as twoset-cookievalues because multiple values may be separated by a;.Changes
When parsing the headers,
set-cookieheaders are handled using the getSetCookie method.Logic to detect multiple header entries has been removed as multiple header values are appended to a single header (via comma).
Issue number: closes #4986
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
Disclaimer: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful.