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

fetch-networking: fix parsing of HTTP headers #1182

Merged
merged 7 commits into from
Nov 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 43 additions & 5 deletions src/browser/fetch_network.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,14 @@ async function on_data_http(data)

let req_headers = new Headers();
for(let i = 1; i < headers.length; ++i) {
let parts = headers[i].split(": ");
let key = parts[0].toLowerCase();
let value = parts[1];
if( key === "host" ) target.host = value;
else if( key.length > 1 ) req_headers.set(parts[0], value);
const header = this.net.parse_http_header(headers[i]);
if(!header) {
console.warn('The request contains an invalid header: "%s"', headers[i]);
this.write(new TextEncoder().encode("HTTP/1.1 400 Bad Request\r\nContent-Length: 0"));
return;
}
SuperMaxusa marked this conversation as resolved.
Show resolved Hide resolved
if( header.key.toLowerCase() === "host" ) target.host = header.value;
else req_headers.append(header.key, header.value);
}

dbg_log("HTTP Dispatch: " + target.href, LOG_FETCH);
Expand Down Expand Up @@ -151,6 +154,41 @@ FetchNetworkAdapter.prototype.fetch = async function(url, options)
}
};

FetchNetworkAdapter.prototype.parse_http_header = function(header)
{
const parts = header.match(/^([^:]*):(.*)$/);
if(!parts) {
dbg_log("Unable to parse HTTP header", LOG_FETCH);
return;
}

const key = parts[1];
const value = parts[2].trim();

if(key.length === 0)
{
dbg_log("Header key is empty, raw header", LOG_FETCH);
return;
}
if(value.length === 0)
{
dbg_log("Header value is empty", LOG_FETCH);
return;
}
if(!/^[\w-]+$/.test(key))
{
dbg_log("Header key contains forbidden characters", LOG_FETCH);
return;
}
if(!/^[\x20-\x7E]+$/.test(value))
{
dbg_log("Header value contains forbidden characters", LOG_FETCH);
return;
}

return { key, value };
};

/**
* @param {Uint8Array} data
*/
Expand Down
40 changes: 39 additions & 1 deletion tests/devices/fetch_network.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,45 @@ const tests =
assert(/This domain is for use in illustrative examples in documents/.test(capture), "got example.org text");
},
},

{
name: "Forbidden character in header name",
start: () =>
{
emulator.serial0_send("wget --header='test.v86: 123' -T 10 -O - test.domain\n");
emulator.serial0_send("echo -e done\\\\tincorrect header name\n");
},
end_trigger: "done\tincorrect header name",
end: (capture) =>
{
assert(/400 Bad Request/.test(capture), "got error 400");
},
},
{
name: "Empty header value",
start: () =>
{
emulator.serial0_send("wget --header='test:' -T 10 -O - test.domain\n");
emulator.serial0_send("echo -e done\\\\tempty header value\n");
},
end_trigger: "done\tempty header value",
end: (capture) =>
{
assert(/400 Bad Request/.test(capture), "got error 400");
},
},
{
name: "Header without separator",
start: () =>
{
emulator.serial0_send("wget --spider --header='testheader' -T 10 -O - test.domain\n");
emulator.serial0_send("echo -e done\\\\theader without colon\n");
},
end_trigger: "done\theader without colon",
end: (capture) =>
{
assert(/400 Bad Request/.test(capture), "got error 400");
},
}
];

const emulator = new V86({
Expand Down
Loading