1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-01 16:51:13 -05:00

Add illegal header name and value guards (#1375)

This commit is contained in:
Kevin (Kun) "Kassimo" Qian 2018-12-19 02:57:23 -05:00 committed by Ryan Dahl
parent 22874d44a6
commit 57338d98be
3 changed files with 85 additions and 1 deletions

View file

@ -2,6 +2,11 @@
import * as domTypes from "./dom_types"; import * as domTypes from "./dom_types";
import { DomIterableMixin } from "./mixins/dom_iterable"; import { DomIterableMixin } from "./mixins/dom_iterable";
// From node-fetch
// Copyright (c) 2016 David Frank. MIT License.
const invalidTokenRegex = /[^\^_`a-zA-Z\-0-9!#$%&'*+.|~]/;
const invalidHeaderCharRegex = /[^\t\x20-\x7e\x80-\xff]/;
// tslint:disable-next-line:no-any // tslint:disable-next-line:no-any
function isHeaders(value: any): value is domTypes.Headers { function isHeaders(value: any): value is domTypes.Headers {
return value instanceof Headers; return value instanceof Headers;
@ -12,6 +17,8 @@ const headerMap = Symbol("header map");
// ref: https://fetch.spec.whatwg.org/#dom-headers // ref: https://fetch.spec.whatwg.org/#dom-headers
class HeadersBase { class HeadersBase {
private [headerMap]: Map<string, string>; private [headerMap]: Map<string, string>;
// TODO: headerGuard? Investigate if it is needed
// node-fetch did not implement this but it is in the spec
private _normalizeParams(name: string, value?: string): string[] { private _normalizeParams(name: string, value?: string): string[] {
name = String(name).toLowerCase(); name = String(name).toLowerCase();
@ -19,6 +26,20 @@ class HeadersBase {
return [name, value]; return [name, value];
} }
// The following name/value validations are copied from
// https://github.com/bitinn/node-fetch/blob/master/src/headers.js
// Copyright (c) 2016 David Frank. MIT License.
private _validateName(name: string) {
if (invalidTokenRegex.test(name)) {
throw new TypeError(`${name} is not a legal HTTP header name`);
}
}
private _validateValue(value: string) {
if (invalidHeaderCharRegex.test(value)) {
throw new TypeError(`${value} is not a legal HTTP header value`);
}
}
constructor(init?: domTypes.HeadersInit) { constructor(init?: domTypes.HeadersInit) {
if (init === null) { if (init === null) {
throw new TypeError( throw new TypeError(
@ -31,6 +52,8 @@ class HeadersBase {
if (Array.isArray(init)) { if (Array.isArray(init)) {
for (const [rawName, rawValue] of init) { for (const [rawName, rawValue] of init) {
const [name, value] = this._normalizeParams(rawName, rawValue); const [name, value] = this._normalizeParams(rawName, rawValue);
this._validateName(name);
this._validateValue(value);
const existingValue = this[headerMap].get(name); const existingValue = this[headerMap].get(name);
this[headerMap].set( this[headerMap].set(
name, name,
@ -42,14 +65,19 @@ class HeadersBase {
for (const rawName of names) { for (const rawName of names) {
const rawValue = init[rawName]; const rawValue = init[rawName];
const [name, value] = this._normalizeParams(rawName, rawValue); const [name, value] = this._normalizeParams(rawName, rawValue);
this._validateName(name);
this._validateValue(value);
this[headerMap].set(name, value); this[headerMap].set(name, value);
} }
} }
} }
} }
// ref: https://fetch.spec.whatwg.org/#concept-headers-append
append(name: string, value: string): void { append(name: string, value: string): void {
const [newname, newvalue] = this._normalizeParams(name, value); const [newname, newvalue] = this._normalizeParams(name, value);
this._validateName(newname);
this._validateValue(newvalue);
const v = this[headerMap].get(newname); const v = this[headerMap].get(newname);
const str = v ? `${v}, ${newvalue}` : newvalue; const str = v ? `${v}, ${newvalue}` : newvalue;
this[headerMap].set(newname, str); this[headerMap].set(newname, str);
@ -57,22 +85,27 @@ class HeadersBase {
delete(name: string): void { delete(name: string): void {
const [newname] = this._normalizeParams(name); const [newname] = this._normalizeParams(name);
this._validateName(newname);
this[headerMap].delete(newname); this[headerMap].delete(newname);
} }
get(name: string): string | null { get(name: string): string | null {
const [newname] = this._normalizeParams(name); const [newname] = this._normalizeParams(name);
this._validateName(newname);
const value = this[headerMap].get(newname); const value = this[headerMap].get(newname);
return value || null; return value || null;
} }
has(name: string): boolean { has(name: string): boolean {
const [newname] = this._normalizeParams(name); const [newname] = this._normalizeParams(name);
this._validateName(newname);
return this[headerMap].has(newname); return this[headerMap].has(newname);
} }
set(name: string, value: string): void { set(name: string, value: string): void {
const [newname, newvalue] = this._normalizeParams(name, value); const [newname, newvalue] = this._normalizeParams(name, value);
this._validateName(newname);
this._validateValue(newvalue);
this[headerMap].set(newname, newvalue); this[headerMap].set(newname, newvalue);
} }
} }

View file

@ -177,3 +177,54 @@ test(function headerTypesAvailable() {
const headers = newHeaders(); const headers = newHeaders();
assert(headers instanceof Headers); assert(headers instanceof Headers);
}); });
// tslint:disable-next-line:max-line-length
// Modified from https://github.com/bitinn/node-fetch/blob/7d3293200a91ad52b5ca7962f9d6fd1c04983edb/test/test.js#L2001-L2014
// Copyright (c) 2016 David Frank. MIT License.
test(function headerIllegalReject() {
let errorCount = 0;
try {
new Headers({ "He y": "ok" });
} catch (e) {
errorCount++;
}
try {
new Headers({ "Hé-y": "ok" });
} catch (e) {
errorCount++;
}
try {
new Headers({ "He-y": "ăk" });
} catch (e) {
errorCount++;
}
const headers = new Headers();
try {
headers.append("Hé-y", "ok");
} catch (e) {
errorCount++;
}
try {
headers.delete("Hé-y");
} catch (e) {
errorCount++;
}
try {
headers.get("Hé-y");
} catch (e) {
errorCount++;
}
try {
headers.has("Hé-y");
} catch (e) {
errorCount++;
}
try {
headers.set("Hé-y", "ok");
} catch (e) {
errorCount++;
}
assertEqual(errorCount, 8);
// 'o k' is valid value but invalid name
new Headers({ "He-y": "o k" });
});

View file

@ -21,7 +21,7 @@ export function assertEqual(actual: any, expected: any, msg?: string) {
} }
if (!equal(actual, expected)) { if (!equal(actual, expected)) {
console.error( console.error(
"assertEqual failed. actual = ", "assertEqual failed. actual =",
actual, actual,
"expected =", "expected =",
expected expected