@@ -105,7 +105,7 @@ function validateCookiePath (path) {
105105
106106 if (
107107 code < 0x20 || // exclude CTLs (0-31)
108- code === 0x7F || // DEL
108+ code > 0x7E || // exclude DEL and non-ascii
109109 code === 0x3B // ;
110110 ) {
111111 throw new Error ( 'Invalid cookie path' )
@@ -114,16 +114,80 @@ function validateCookiePath (path) {
114114}
115115
116116/**
117- * I have no idea why these values aren't allowed to be honest,
118- * but Deno tests these. - Khafra
117+ * <let-dig> ::= <letter> | <digit>
118+ *
119+ * <letter> ::= any one of the 52 alphabetic characters A through Z in
120+ * upper case and a through z in lower case
121+ *
122+ * <digit> ::= any one of the ten digits 0 through 9r
123+ *
124+ * @see https://www.rfc-editor.org/rfc/rfc1034#section-3.5
125+ * @param {number } code
126+ */
127+ function isLetterOrDigit ( code ) {
128+ return (
129+ ( code >= 0x30 && code <= 0x39 ) || // 0-9
130+ ( code >= 0x41 && code <= 0x5A ) || // A-Z
131+ ( code >= 0x61 && code <= 0x7A ) // a-z
132+ )
133+ }
134+
135+ /**
136+ * Validates a cookie domain against the "preferred name syntax".
137+ *
138+ * <domain> ::= <subdomain> | " "
139+ * <subdomain> ::= <label> | <subdomain> "." <label>
140+ * <label> ::= <let-dig> [ [ <ldh-str> ] <let-dig> ]
141+ * <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
142+ * <let-dig-hyp> ::= <let-dig> | "-"
143+ *
144+ * @see https://www.rfc-editor.org/rfc/rfc1034#section-3.5
145+ * @see https://www.rfc-editor.org/rfc/rfc1123#section-2.1
146+ * @see https://www.rfc-editor.org/rfc/rfc1035#section-2.3.4
119147 * @param {string } domain
120148 */
121149function validateCookieDomain ( domain ) {
122- if (
123- domain . startsWith ( '-' ) ||
124- domain . endsWith ( '.' ) ||
125- domain . endsWith ( '-' )
126- ) {
150+ // <domain> ::= <subdomain> | " "
151+ if ( domain === ' ' ) {
152+ return
153+ }
154+
155+ if ( domain . length > 255 ) {
156+ throw new Error ( 'Invalid cookie domain' )
157+ }
158+
159+ let labelLength = 0
160+
161+ for ( let i = 0 ; i < domain . length ; ++ i ) {
162+ const code = domain . charCodeAt ( i )
163+
164+ if ( code === 0x2E ) {
165+ if ( labelLength === 0 ) {
166+ throw new Error ( 'Invalid cookie domain' )
167+ }
168+
169+ if ( domain . charCodeAt ( i - 1 ) === 0x2D ) { // "-"
170+ throw new Error ( 'Invalid cookie domain' )
171+ }
172+
173+ labelLength = 0
174+ continue
175+ }
176+
177+ if ( labelLength === 0 && ! isLetterOrDigit ( code ) ) {
178+ throw new Error ( 'Invalid cookie domain' )
179+ }
180+
181+ if ( ! isLetterOrDigit ( code ) && code !== 0x2D ) { // "-"
182+ throw new Error ( 'Invalid cookie domain' )
183+ }
184+
185+ if ( ++ labelLength > 63 ) {
186+ throw new Error ( 'Invalid cookie domain' )
187+ }
188+ }
189+
190+ if ( labelLength === 0 || domain . charCodeAt ( domain . length - 1 ) === 0x2D ) { // "-"
127191 throw new Error ( 'Invalid cookie domain' )
128192 }
129193}
@@ -266,7 +330,13 @@ function stringify (cookie) {
266330
267331 const [ key , ...value ] = part . split ( '=' )
268332
269- out . push ( `${ key . trim ( ) } =${ value . join ( '=' ) } ` )
333+ const trimmedKey = key . trim ( )
334+ const joinedValue = value . join ( '=' )
335+
336+ validateCookieName ( trimmedKey )
337+ validateCookieValue ( joinedValue )
338+
339+ out . push ( `${ trimmedKey } =${ joinedValue } ` )
270340 }
271341
272342 return out . join ( '; ' )
0 commit comments