Skip to content

Commit

Permalink
cookies: improve validateCookieName (#2884)
Browse files Browse the repository at this point in the history
  • Loading branch information
Uzlopak committed Feb 29, 2024
1 parent 6cd5f09 commit 4106830
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 20 deletions.
12 changes: 12 additions & 0 deletions benchmarks/cookies/validate-cookie-name.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { bench, group, run } from 'mitata'
import { validateCookieName } from '../../lib/web/cookies/util.js'

const valid = 'Cat'

group('validateCookieName', () => {
bench(`valid: ${valid}`, () => {
return validateCookieName(valid)
})
})

await run()
42 changes: 22 additions & 20 deletions lib/web/cookies/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,28 +32,29 @@ function isCTLExcludingHtab (value) {
* @param {string} name
*/
function validateCookieName (name) {
for (const char of name) {
const code = char.charCodeAt(0)
for (let i = 0; i < name.length; ++i) {
const code = name.charCodeAt(i)

if (
(code <= 0x20 || code > 0x7F) ||
char === '(' ||
char === ')' ||
char === '>' ||
char === '<' ||
char === '@' ||
char === ',' ||
char === ';' ||
char === ':' ||
char === '\\' ||
char === '"' ||
char === '/' ||
char === '[' ||
char === ']' ||
char === '?' ||
char === '=' ||
char === '{' ||
char === '}'
code < 0x21 || // exclude CTLs (0-31), SP and HT
code > 0x7E || // exclude non-ascii and DEL
code === 0x22 || // "
code === 0x28 || // (
code === 0x29 || // )
code === 0x3C || // <
code === 0x3E || // >
code === 0x40 || // @
code === 0x2C || // ,
code === 0x3B || // ;
code === 0x3A || // :
code === 0x5C || // \
code === 0x2F || // /
code === 0x5B || // [
code === 0x5D || // ]
code === 0x3F || // ?
code === 0x3D || // =
code === 0x7B || // {
code === 0x7D // }
) {
throw new Error('Invalid cookie name')
}
Expand Down Expand Up @@ -297,6 +298,7 @@ function getHeadersList (headers) {

module.exports = {
isCTLExcludingHtab,
validateCookieName,
validateCookiePath,
validateCookieValue,
toIMFDate,
Expand Down
130 changes: 130 additions & 0 deletions test/cookie/validate-cookie-name.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
'use strict'

const { test, describe } = require('node:test')
const { throws, strictEqual } = require('node:assert')

const {
validateCookieName
} = require('../../lib/web/cookies/util')

describe('validateCookieName', () => {
test('should throw for CTLs', () => {
throws(() => validateCookieName('\x00'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x01'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x02'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x03'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x04'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x05'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x06'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x07'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x08'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x09'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x0A'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x0B'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x0C'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x0D'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x0E'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x0F'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x10'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x11'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x12'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x13'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x14'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x15'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x16'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x17'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x18'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x19'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x1A'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x1B'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x1C'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x1D'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x1E'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x1F'), new Error('Invalid cookie name'))
throws(() => validateCookieName('\x7F'), new Error('Invalid cookie name'))
})

test('should throw for " " character', () => {
throws(() => validateCookieName(' '), new Error('Invalid cookie name'))
})

test('should throw for Horizontal Tab character', () => {
throws(() => validateCookieName('\t'), new Error('Invalid cookie name'))
})

test('should throw for ; character', () => {
throws(() => validateCookieName(';'), new Error('Invalid cookie name'))
})

test('should throw for " character', () => {
throws(() => validateCookieName('"'), new Error('Invalid cookie name'))
})

test('should throw for , character', () => {
throws(() => validateCookieName(','), new Error('Invalid cookie name'))
})

test('should throw for \\ character', () => {
throws(() => validateCookieName('\\'), new Error('Invalid cookie name'))
})

test('should throw for ( character', () => {
throws(() => validateCookieName('('), new Error('Invalid cookie name'))
})

test('should throw for ) character', () => {
throws(() => validateCookieName(')'), new Error('Invalid cookie name'))
})

test('should throw for < character', () => {
throws(() => validateCookieName('<'), new Error('Invalid cookie name'))
})

test('should throw for > character', () => {
throws(() => validateCookieName('>'), new Error('Invalid cookie name'))
})

test('should throw for @ character', () => {
throws(() => validateCookieName('@'), new Error('Invalid cookie name'))
})

test('should throw for : character', () => {
throws(() => validateCookieName(':'), new Error('Invalid cookie name'))
})

test('should throw for / character', () => {
throws(() => validateCookieName('/'), new Error('Invalid cookie name'))
})

test('should throw for [ character', () => {
throws(() => validateCookieName('['), new Error('Invalid cookie name'))
})

test('should throw for ] character', () => {
throws(() => validateCookieName(']'), new Error('Invalid cookie name'))
})

test('should throw for ? character', () => {
throws(() => validateCookieName('?'), new Error('Invalid cookie name'))
})

test('should throw for = character', () => {
throws(() => validateCookieName('='), new Error('Invalid cookie name'))
})

test('should throw for { character', () => {
throws(() => validateCookieName('{'), new Error('Invalid cookie name'))
})

test('should throw for } character', () => {
throws(() => validateCookieName('}'), new Error('Invalid cookie name'))
})

test('should pass for a printable character', t => {
strictEqual(validateCookieName('A'), undefined)
strictEqual(validateCookieName('Z'), undefined)
strictEqual(validateCookieName('a'), undefined)
strictEqual(validateCookieName('z'), undefined)
strictEqual(validateCookieName('!'), undefined)
})
})

0 comments on commit 4106830

Please sign in to comment.