mirror of
https://github.com/denoland/deno.git
synced 2024-11-25 15:29:32 -05:00
Add illegal header name and value guards (#1375)
This commit is contained in:
parent
22874d44a6
commit
57338d98be
3 changed files with 85 additions and 1 deletions
|
@ -2,6 +2,11 @@
|
|||
import * as domTypes from "./dom_types";
|
||||
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
|
||||
function isHeaders(value: any): value is domTypes.Headers {
|
||||
return value instanceof Headers;
|
||||
|
@ -12,6 +17,8 @@ const headerMap = Symbol("header map");
|
|||
// ref: https://fetch.spec.whatwg.org/#dom-headers
|
||||
class HeadersBase {
|
||||
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[] {
|
||||
name = String(name).toLowerCase();
|
||||
|
@ -19,6 +26,20 @@ class HeadersBase {
|
|||
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) {
|
||||
if (init === null) {
|
||||
throw new TypeError(
|
||||
|
@ -31,6 +52,8 @@ class HeadersBase {
|
|||
if (Array.isArray(init)) {
|
||||
for (const [rawName, rawValue] of init) {
|
||||
const [name, value] = this._normalizeParams(rawName, rawValue);
|
||||
this._validateName(name);
|
||||
this._validateValue(value);
|
||||
const existingValue = this[headerMap].get(name);
|
||||
this[headerMap].set(
|
||||
name,
|
||||
|
@ -42,14 +65,19 @@ class HeadersBase {
|
|||
for (const rawName of names) {
|
||||
const rawValue = init[rawName];
|
||||
const [name, value] = this._normalizeParams(rawName, rawValue);
|
||||
this._validateName(name);
|
||||
this._validateValue(value);
|
||||
this[headerMap].set(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ref: https://fetch.spec.whatwg.org/#concept-headers-append
|
||||
append(name: string, value: string): void {
|
||||
const [newname, newvalue] = this._normalizeParams(name, value);
|
||||
this._validateName(newname);
|
||||
this._validateValue(newvalue);
|
||||
const v = this[headerMap].get(newname);
|
||||
const str = v ? `${v}, ${newvalue}` : newvalue;
|
||||
this[headerMap].set(newname, str);
|
||||
|
@ -57,22 +85,27 @@ class HeadersBase {
|
|||
|
||||
delete(name: string): void {
|
||||
const [newname] = this._normalizeParams(name);
|
||||
this._validateName(newname);
|
||||
this[headerMap].delete(newname);
|
||||
}
|
||||
|
||||
get(name: string): string | null {
|
||||
const [newname] = this._normalizeParams(name);
|
||||
this._validateName(newname);
|
||||
const value = this[headerMap].get(newname);
|
||||
return value || null;
|
||||
}
|
||||
|
||||
has(name: string): boolean {
|
||||
const [newname] = this._normalizeParams(name);
|
||||
this._validateName(newname);
|
||||
return this[headerMap].has(newname);
|
||||
}
|
||||
|
||||
set(name: string, value: string): void {
|
||||
const [newname, newvalue] = this._normalizeParams(name, value);
|
||||
this._validateName(newname);
|
||||
this._validateValue(newvalue);
|
||||
this[headerMap].set(newname, newvalue);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -177,3 +177,54 @@ test(function headerTypesAvailable() {
|
|||
const headers = newHeaders();
|
||||
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" });
|
||||
});
|
||||
|
|
|
@ -21,7 +21,7 @@ export function assertEqual(actual: any, expected: any, msg?: string) {
|
|||
}
|
||||
if (!equal(actual, expected)) {
|
||||
console.error(
|
||||
"assertEqual failed. actual = ",
|
||||
"assertEqual failed. actual =",
|
||||
actual,
|
||||
"expected =",
|
||||
expected
|
||||
|
|
Loading…
Reference in a new issue