Skip to content
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

bug: header value checks are too strict in fetch #1680

Open
KhafraDev opened this issue Oct 3, 2022 · 3 comments
Open

bug: header value checks are too strict in fetch #1680

KhafraDev opened this issue Oct 3, 2022 · 3 comments
Labels
bug Something isn't working

Comments

@KhafraDev
Copy link
Member

KhafraDev commented Oct 3, 2022

Bug Description

const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/

The regex here is too strict for fetch which causes a bunch of tests to fail:

{ fail: '\x01' }
{ fail: '\x02' }
{ fail: '\x03' }
{ fail: '\x04' }
{ fail: '\x05' }
{ fail: '\x06' }
{ fail: '\x07' }
{ fail: '\b' }
{ fail: '\x0E' }
{ fail: '\x0F' }
{ fail: '\x10' }
{ fail: '\x11' }
{ fail: '\x12' }
{ fail: '\x13' }
{ fail: '\x14' }
{ fail: '\x15' }
{ fail: '\x16' }
{ fail: '\x17' }
{ fail: '\x18' }
{ fail: '\x19' }
{ fail: '\x1A' }
{ fail: '\x1B' }
{ fail: '\x1C' }
{ fail: '\x1D' }
{ fail: '\x1E' }
{ fail: '\x1F' }

Reproducible By

https://gist.github.com/KhafraDev/cc178abba0a89580008f44c7ebee76a3

Sorry for the messy code it was taken almost directly from wpt (https://github.com/web-platform-tests/wpt/blob/master/fetch/api/headers/header-values-normalize.any.js)

Expected Behavior

The tests should pass.

Logs & Screenshots

Environment

Additional context

These checks were added in a29a151 to fix an issue where headers weren't being sanitized.

cc @mcollina

According to the spec, a header value is a string (or Uint8Array, etc) that has no leading or trailing whitespace and doesn't contain \0 or LF or CR.

Also see httpwg/http-core#19 (comment)

and the spec

The definition of header value is not defined in terms of an HTTP token production as it is broken
@KhafraDev KhafraDev added the bug Something isn't working label Oct 3, 2022
@mcollina
Copy link
Member

mcollina commented Oct 4, 2022

My understanding is that the use case of fetch in the browser vs in the server is different and it might be that those chars are safe to render on the wire from browsers but not from servers. However, that regexp might be too strict.

If you'd like to loose it a bit, it's fine as long as the tests pass.

@KhafraDev
Copy link
Member Author

The server is responding with the headers sent from fetch so there is an expectation that both the server and fetch can receive these headers. I will get around to fixing this, but wasn't entirely sure. 👍

@KhafraDev
Copy link
Member Author

KhafraDev commented Oct 4, 2022

I looked into it and there seems to be something preventing the request from being sent; I confirmed that the headers are being written to the socket, but can't figure out much more than that.

Somewhere along the lines 'HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n' is being sent (maybe not to the socket?) and we end up with only a connection: close header. No request is sent to the server.

Headers written to socket:

{
  header: 'GET /?headers=val1|val2|val3 HTTP/1.1\r\n' +
    'host: localhost:64375\r\n' +
    'connection: keep-alive\r\n' +
    'val1: \x01\r\n' +
    'val2: x\x01\r\n' +
    'val3: \x01x\r\n' +
    'accept: */*\r\n' +
    'accept-language: *\r\n' +
    'sec-fetch-mode: cors\r\n' +
    'user-agent: undici\r\n' +
    'accept-encoding: gzip, deflate\r\n'
}

here:

socket.write(`${header}\r\n`, 'ascii')

diff

diff --git a/lib/core/request.js b/lib/core/request.js
index b05a16d..959b942 100644
--- a/lib/core/request.js
+++ b/lib/core/request.js
@@ -18,12 +18,11 @@ const util = require('./util')
 const tokenRegExp = /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/

 /**
- * Matches if val contains an invalid field-vchar
- *  field-value    = *( field-content / obs-fold )
- *  field-content  = field-vchar [ 1*( SP / HTAB ) field-vchar ]
- *  field-vchar    = VCHAR / obs-text
+ * A header value is a byte sequence that matches the following conditions:
+ * * Has no leading or trailing HTTP tab or space bytes.
+ * * Contains no 0x00 (NUL) or HTTP newline bytes.
  */
-const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/
+const headerCharRegex = /^(\x09|\x20)|(\0|\r|\n)|(\x09|\x20)$/

 // Verifies that a given path is valid does not contain control chars \x00 to \x20
 const invalidPathRegex = /[^\u0021-\u00ff]/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants