Skip to content

Commit ded1630

Browse files
authored
chore: deprecate pathless cookie, warn on path: '' (#11237)
This prints a warning if you call cookies.set(...) or cookies.delete(...) without specifying a path, as part of #9299.
1 parent 24a16ce commit ded1630

File tree

4 files changed

+61
-16
lines changed

4 files changed

+61
-16
lines changed

.changeset/real-carrots-share.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/kit': patch
3+
---
4+
5+
chore: deprecate cookies.set/delete without path option

packages/kit/src/runtime/server/cookie.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { parse, serialize } from 'cookie';
22
import { normalize_path } from '../../utils/url.js';
3+
import { warn_with_callsite } from './utils.js';
34

45
/**
56
* Tracks all cookies set during dev mode so we can emit warnings
@@ -14,6 +15,27 @@ const cookie_paths = {};
1415
*/
1516
const MAX_COOKIE_SIZE = 4129;
1617

18+
/**
19+
*
20+
* @param {import('cookie').CookieSerializeOptions} opts
21+
* @param {'set' | 'delete'} method
22+
*/
23+
function deprecate_missing_path(opts, method) {
24+
if (opts.path === undefined) {
25+
warn_with_callsite(
26+
`Calling \`cookies.${method}}(...)\` without specifying a \`path\` is deprecated, and will be disallowed in SvelteKit 2.0. Relative paths can be used`,
27+
1
28+
);
29+
}
30+
31+
if (opts.path === '') {
32+
warn_with_callsite(
33+
`Calling \`cookies.${method}(...)\` with \`path: ''\` will behave differently in SvelteKit 2.0. Instead of using the browser default behaviour, it will set the cookie path to the current pathname`,
34+
1
35+
);
36+
}
37+
}
38+
1739
/**
1840
* @param {Request} request
1941
* @param {URL} url
@@ -107,6 +129,7 @@ export function get_cookies(request, url, trailing_slash) {
107129
* @param {import('cookie').CookieSerializeOptions} opts
108130
*/
109131
set(name, value, opts = {}) {
132+
deprecate_missing_path(opts, 'set');
110133
set_internal(name, value, { ...defaults, ...opts });
111134
},
112135

@@ -115,7 +138,10 @@ export function get_cookies(request, url, trailing_slash) {
115138
* @param {import('cookie').CookieSerializeOptions} opts
116139
*/
117140
delete(name, opts = {}) {
141+
deprecate_missing_path(opts, 'delete');
142+
118143
cookies.set(name, '', {
144+
path: default_path, // TODO 2.0 remove this
119145
...opts,
120146
maxAge: 0
121147
});

packages/kit/src/runtime/server/cookie.spec.js

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,15 @@ const cookies_setup = ({ href, headers } = {}) => {
5858

5959
test('a cookie should not be present after it is deleted', () => {
6060
const { cookies } = cookies_setup();
61-
cookies.set('a', 'b');
61+
cookies.set('a', 'b', { path: '/' });
6262
expect(cookies.get('a')).toEqual('b');
63-
cookies.delete('a');
63+
cookies.delete('a', { path: '/' });
6464
assert.isNotOk(cookies.get('a'));
6565
});
6666

6767
test('default values when set is called', () => {
6868
const { cookies, new_cookies } = cookies_setup();
69-
cookies.set('a', 'b');
69+
cookies.set('a', 'b', { path: '/' });
7070
const opts = new_cookies['a']?.options;
7171
assert.equal(opts?.secure, true);
7272
assert.equal(opts?.httpOnly, true);
@@ -86,7 +86,7 @@ test('default values when set is called on sub path', () => {
8686

8787
test('default values when on localhost', () => {
8888
const { cookies, new_cookies } = cookies_setup({ href: 'http://localhost:1234' });
89-
cookies.set('a', 'b');
89+
cookies.set('a', 'b', { path: '/' });
9090
const opts = new_cookies['a']?.options;
9191
assert.equal(opts?.secure, false);
9292
});
@@ -103,7 +103,7 @@ test('overridden defaults when set is called', () => {
103103

104104
test('default values when delete is called', () => {
105105
const { cookies, new_cookies } = cookies_setup();
106-
cookies.delete('a');
106+
cookies.delete('a', { path: '/' });
107107
const opts = new_cookies['a']?.options;
108108
assert.equal(opts?.secure, true);
109109
assert.equal(opts?.httpOnly, true);
@@ -125,24 +125,24 @@ test('overridden defaults when delete is called', () => {
125125

126126
test('cannot override maxAge on delete', () => {
127127
const { cookies, new_cookies } = cookies_setup();
128-
cookies.delete('a', { maxAge: 1234 });
128+
cookies.delete('a', { path: '/', maxAge: 1234 });
129129
const opts = new_cookies['a']?.options;
130130
assert.equal(opts?.maxAge, 0);
131131
});
132132

133133
test('last cookie set with the same name wins', () => {
134134
const { cookies, new_cookies } = cookies_setup();
135-
cookies.set('a', 'foo');
136-
cookies.set('a', 'bar');
135+
cookies.set('a', 'foo', { path: '/' });
136+
cookies.set('a', 'bar', { path: '/' });
137137
const entry = new_cookies['a'];
138138
assert.equal(entry?.value, 'bar');
139139
});
140140

141141
test('cookie names are case sensitive', () => {
142142
const { cookies, new_cookies } = cookies_setup();
143143
// not that one should do this, but we follow the spec...
144-
cookies.set('a', 'foo');
145-
cookies.set('A', 'bar');
144+
cookies.set('a', 'foo', { path: '/' });
145+
cookies.set('A', 'bar', { path: '/' });
146146
const entrya = new_cookies['a'];
147147
const entryA = new_cookies['A'];
148148
assert.equal(entrya?.value, 'foo');
@@ -157,8 +157,8 @@ test('serialized cookie header should be url-encoded', () => {
157157
cookie: 'a=f%C3%BC; b=foo+bar' // a=fü
158158
}
159159
});
160-
cookies.set('c', 'fö'); // should use default encoding
161-
cookies.set('d', 'fö', { encode: () => 'öf' }); // should respect `encode`
160+
cookies.set('c', 'fö', { path: '/' }); // should use default encoding
161+
cookies.set('d', 'fö', { path: '/', encode: () => 'öf' }); // should respect `encode`
162162
const header = get_cookie_header(new URL(href), 'e=f%C3%A4; f=foo+bar');
163163
assert.equal(header, 'a=f%C3%BC; b=foo+bar; c=f%C3%B6; d=öf; e=f%C3%A4; f=foo+bar');
164164
});
@@ -169,7 +169,7 @@ test('warns if cookie exceeds 4,129 bytes', () => {
169169

170170
try {
171171
const { cookies } = cookies_setup();
172-
cookies.set('a', 'a'.repeat(4097));
172+
cookies.set('a', 'a'.repeat(4097), { path: '/' });
173173
} catch (e) {
174174
const error = /** @type {Error} */ (e);
175175

@@ -184,9 +184,9 @@ test('get all cookies from header and set calls', () => {
184184
const { cookies } = cookies_setup();
185185
expect(cookies.getAll()).toEqual([{ name: 'a', value: 'b' }]);
186186

187-
cookies.set('a', 'foo');
188-
cookies.set('a', 'bar');
189-
cookies.set('b', 'baz');
187+
cookies.set('a', 'foo', { path: '/' });
188+
cookies.set('a', 'bar', { path: '/' });
189+
cookies.set('b', 'baz', { path: '/' });
190190

191191
expect(cookies.getAll()).toEqual([
192192
{ name: 'a', value: 'bar' },

packages/kit/src/runtime/server/utils.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,3 +159,17 @@ export function stringify_uses(node) {
159159

160160
return `"uses":{${uses.join(',')}}`;
161161
}
162+
163+
/**
164+
* @param {string} message
165+
* @param {number} offset
166+
*/
167+
export function warn_with_callsite(message, offset = 0) {
168+
if (DEV) {
169+
const stack = fix_stack_trace(new Error()).split('\n');
170+
const line = stack.at(3 + offset);
171+
message += `\n${line}`;
172+
}
173+
174+
console.warn(message);
175+
}

0 commit comments

Comments
 (0)