From 9c9982a60ffa10dd3e84c31d14eee5093e3268b5 Mon Sep 17 00:00:00 2001 From: "Yasser A.Idrissi" Date: Tue, 1 Dec 2020 14:23:03 +0100 Subject: [PATCH] feat(http): Add Cookie value validation (denoland/deno#8471) --- http/cookie.ts | 28 ++++++++++++++++++++++++++++ http/cookie_test.ts | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/http/cookie.ts b/http/cookie.ts index fb0c2dee0629..48895a2b16e2 100644 --- a/http/cookie.ts +++ b/http/cookie.ts @@ -40,6 +40,7 @@ function toString(cookie: Cookie): string { } const out: string[] = []; validateCookieName(cookie.name); + validateCookieValue(cookie.name, cookie.value); out.push(`${cookie.name}=${cookie.value}`); // Fallback for invalid Set-Cookie @@ -114,6 +115,33 @@ function validatePath(path: string | null): void { } } +/** + *Validate Cookie Value. + * @see https://tools.ietf.org/html/rfc6265#section-4.1 + * @param value Cookie value. + */ +function validateCookieValue(name: string, value: string | null): void { + if (value == null || name == null) return; + for (let i = 0; i < value.length; i++) { + const c = value.charAt(i); + if ( + c < String.fromCharCode(0x21) || c == String.fromCharCode(0x22) || + c == String.fromCharCode(0x2c) || c == String.fromCharCode(0x3b) || + c == String.fromCharCode(0x5c) || c == String.fromCharCode(0x7f) + ) { + throw new Error( + "RFC2616 cookie '" + name + "' cannot have '" + c + "' as value", + ); + } + if (c > String.fromCharCode(0x80)) { + throw new Error( + "RFC2616 cookie '" + name + "' can only have US-ASCII chars as value" + + c.charCodeAt(0).toString(16), + ); + } + } +} + /** * Parse the cookies of the Server Request * @param req An object which has a `headers` property diff --git a/http/cookie_test.ts b/http/cookie_test.ts index bc45b2996455..09d6764af6cf 100644 --- a/http/cookie_test.ts +++ b/http/cookie_test.ts @@ -65,6 +65,44 @@ Deno.test({ }, }); +Deno.test({ + name: "Cookie Value Validation", + fn(): void { + const res: Response = {}; + const tokens = [ + "1f\tWa", + "\t", + "1f Wa", + "1f;Wa", + '"1fWa', + "1f\\Wa", + '1f"Wa', + '"', + "1fWa\u0005", + "1f\u0091Wa", + ]; + res.headers = new Headers(); + tokens.forEach((value) => { + assertThrows( + (): void => { + setCookie( + res, + { + name: "Space", + value, + httpOnly: true, + secure: true, + maxAge: 3, + }, + ); + }, + Error, + "RFC2616 cookie 'Space'", + ); + }); + }, +}); + Deno.test({ name: "Cookie Path Validation", fn(): void {