Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: should not set sameSite and CHIPS when secure = false #45

Merged
merged 4 commits into from Dec 28, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 9 additions & 4 deletions lib/cookies.js
Expand Up @@ -115,14 +115,18 @@ class Cookies {
// https://github.com/linsight/should-send-same-site-none
// fixed SameSite=None: Known Incompatible Clients
const userAgent = this.ctx.get('user-agent');
let isSameSiteNone = false;
if (opts.sameSite && typeof opts.sameSite === 'string' && opts.sameSite.toLowerCase() === 'none') {
if (!this.secure || (userAgent && !this.isSameSiteNoneCompatible(userAgent))) {
isSameSiteNone = true;
if (opts.secure === false || !this.secure || (userAgent && !this.isSameSiteNoneCompatible(userAgent))) {
// Non-secure context or Incompatible clients, don't send SameSite=None property
opts.sameSite = false;
isSameSiteNone = false;
}
}
if (opts.partitioned) {
fengmk2 marked this conversation as resolved.
Show resolved Hide resolved
if (!this.secure || (userAgent && !this.isPartitionedCompatible(userAgent))) {
// allow to set partitioned: secure=true and sameSite=none and chrome >= 118
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

需要满足3个条件才能设置 partitioned

if (!isSameSiteNone || opts.secure === false || !this.secure || (userAgent && !this.isPartitionedCompatible(userAgent))) {
// Non-secure context or Incompatible clients, don't send partitioned property
opts.partitioned = false;
}
Expand Down Expand Up @@ -182,10 +186,11 @@ class Cookies {
}

isPartitionedCompatible(userAgent) {
// Chrome >= 114.0.0.0
// support: Chrome >= 114.0.0.0
// default enable: Chrome >= 118.0.0.0
// https://developers.google.com/privacy-sandbox/3pcd/chips
const result = this._parseChromiumAndMajorVersion(userAgent);
if (result.chromium) return result.majorVersion >= 114;
if (result.chromium) return result.majorVersion >= 118;
return false;
}
}
Expand Down
139 changes: 107 additions & 32 deletions test/lib/cookies.test.js
Expand Up @@ -392,13 +392,31 @@ describe('test/lib/cookies.test.js', () => {
}
});

it('should not send SameSite=none property on options.secure = false', () => {
const cookies = Cookies({
secure: true,
headers: {
'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_0 like Mac OS X) AppleWebKit/602.1.38 (KHTML, like Gecko) Version/66.6 Mobile/14A5297c Safari/602.1',
},
}, { secure: true }, { sameSite: 'none' });

const opts = {
signed: 1,
secure: false,
};
cookies.set('foo', 'hello', opts);

assert(opts.signed === 1);
assert(opts.secure === false);
assert(cookies.ctx.response.headers['set-cookie'].join(';').match(/foo=hello/));
for (const str of cookies.ctx.response.headers['set-cookie']) {
assert(str.includes('; path=/; httponly'));
}
});

describe('opts.partitioned', () => {
it('should not send partitioned property on incompatible clients', () => {
const userAgents = [
'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML%2C like Gecko) Chrome/64.0.3282.140 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Safari/537.36',
'Mozilla/5.0 (Linux; U; Android 8.1.0; zh-CN; OE106 Build/OPM1.171019.026) AppleWebKit/537.36 (KHTML%2C like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/11.9.4.974 UWS/2.13.2.90 Mobile Safari/537.36 AliApp(DingTalk/4.7.18) com.alibaba.android.rimet/12362010 Channel/1565683214685 language/zh-CN UT4Aplus/0.2.25',
'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML%2C like Gecko) Chrome/63.0.3239.132 Safari/537.36 dingtalk-win/1.0.0 nw(0.14.7) DingTalk(4.7.19-Release.16) Mojo/1.0.0 Native AppType(release)',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML%2C like Gecko) Chrome/62.0.3202.94 Safari/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML%2C like Gecko) Chrome/52.0.2723.2 Safari/537.36',
];
Expand All @@ -408,7 +426,7 @@ describe('test/lib/cookies.test.js', () => {
headers: {
'user-agent': ua,
},
}, { secure: true }, { partitioned: true });
}, { secure: true }, { partitioned: true, sameSite: 'None' });
const opts = {
signed: 1,
};
Expand All @@ -418,18 +436,18 @@ describe('test/lib/cookies.test.js', () => {
assert(opts.secure === undefined);
assert(cookies.ctx.response.headers['set-cookie'].join(';').match(/foo=hello/));
for (const str of cookies.ctx.response.headers['set-cookie']) {
assert(str.includes('; path=/; secure; httponly'));
assert.match(str, /; path=\/; secure; httponly/);
}
}
});

it('should not send partitioned property on Chrome < 114', () => {
it('should not send partitioned property on Chrome < 118', () => {
const cookies = Cookies({
secure: true,
headers: {
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.3945.29 Safari/537.36',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.3945.29 Safari/537.36',
},
}, { secure: true }, { partitioned: true });
}, { secure: true }, { partitioned: true, sameSite: 'None' });
const opts = {
signed: 1,
};
Expand All @@ -439,17 +457,17 @@ describe('test/lib/cookies.test.js', () => {
assert(opts.secure === undefined);
assert(cookies.ctx.response.headers['set-cookie'].join(';').match(/foo=hello/));
for (const str of cookies.ctx.response.headers['set-cookie']) {
assert(str.includes('; path=/; secure; httponly'));
assert(str.includes('; path=/; samesite=none; secure; httponly'));
}
});

it('should send partitioned property on Chrome >= 114', () => {
it('should send partitioned property on Chrome >= 118', () => {
let cookies = Cookies({
secure: true,
headers: {
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.3945.29 Safari/537.36',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.3945.29 Safari/537.36',
},
}, { secure: true }, { partitioned: true });
}, { secure: true }, { partitioned: true, sameSite: 'None' });
const opts = {
signed: 1,
};
Expand All @@ -459,22 +477,22 @@ describe('test/lib/cookies.test.js', () => {
assert(opts.secure === undefined);
assert(cookies.ctx.response.headers['set-cookie'].join(';').match(/foo=hello/));
for (const str of cookies.ctx.response.headers['set-cookie']) {
assert(str.includes('; path=/; secure; httponly; partitioned'));
assert(str.includes('; path=/; samesite=none; secure; httponly; partitioned'));
}

cookies = Cookies({
secure: true,
headers: {
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.3945.29 Safari/537.36',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.3945.29 Safari/537.36',
},
}, { secure: true }, { partitioned: true });
}, { secure: true }, { partitioned: true, sameSite: 'None' });
cookies.set('foo', 'hello', opts);

assert(opts.signed === 1);
assert(opts.secure === undefined);
assert(cookies.ctx.response.headers['set-cookie'].join(';').match(/foo=hello/));
for (const str of cookies.ctx.response.headers['set-cookie']) {
assert(str.includes('; path=/; secure; httponly; partitioned'));
assert(str.includes('; path=/; samesite=none; secure; httponly; partitioned'));
}

// empty user-agent
Expand All @@ -483,14 +501,32 @@ describe('test/lib/cookies.test.js', () => {
headers: {
'user-agent': '',
},
}, { secure: true }, { partitioned: true });
}, { secure: true }, { partitioned: true, sameSite: 'None' });
cookies.set('foo', 'hello', opts);

assert(opts.signed === 1);
assert(opts.secure === undefined);
assert(cookies.ctx.response.headers['set-cookie'].join(';').match(/foo=hello/));
for (const str of cookies.ctx.response.headers['set-cookie']) {
assert(str.includes('; path=/; secure; httponly; partitioned'));
assert(str.includes('; path=/; samesite=none; secure; httponly; partitioned'));
}

cookies = Cookies({
secure: true,
headers: {
'user-agent': '',
},
}, { secure: true });
cookies.set('foo', 'hello', {
sameSite: 'None',
partitioned: true,
});

assert(opts.signed === 1);
assert(opts.secure === undefined);
assert(cookies.ctx.response.headers['set-cookie'].join(';').match(/foo=hello/));
for (const str of cookies.ctx.response.headers['set-cookie']) {
assert(str.includes('; path=/; samesite=none; secure; httponly; partitioned'));
}
});

Expand All @@ -500,7 +536,7 @@ describe('test/lib/cookies.test.js', () => {
headers: {
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.3945.29 Safari/537.36',
},
}, null, { partitioned: true });
}, null, { partitioned: true, sameSite: 'None' });
const opts = {
signed: 1,
};
Expand All @@ -518,9 +554,9 @@ describe('test/lib/cookies.test.js', () => {
const cookies = Cookies({
secure: true,
headers: {
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.3945.29 Safari/537.36',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.3945.29 Safari/537.36',
},
}, { secure: true }, { partitioned: true, removeUnpartitioned: true });
}, { secure: true }, { partitioned: true, removeUnpartitioned: true, sameSite: 'None' });
const opts = {
signed: 1,
};
Expand All @@ -531,19 +567,19 @@ describe('test/lib/cookies.test.js', () => {
const headers = cookies.ctx.response.headers['set-cookie'];
// console.log(headers);
assert.equal(headers.length, 4);
assert.equal(headers[0], 'foo=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; secure; httponly');
assert.equal(headers[1], 'foo.sig=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; secure; httponly');
assert.equal(headers[2], 'foo=hello; path=/; secure; httponly; partitioned');
assert.equal(headers[3], 'foo.sig=ZWbaA4bWk8ByBuYVgfmJ2DMvhhS3sOctMbfXAQ2vnwI; path=/; secure; httponly; partitioned');
assert.equal(headers[0], 'foo=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; samesite=none; secure; httponly');
assert.equal(headers[1], 'foo.sig=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; samesite=none; secure; httponly');
assert.equal(headers[2], 'foo=hello; path=/; samesite=none; secure; httponly; partitioned');
assert.equal(headers[3], 'foo.sig=ZWbaA4bWk8ByBuYVgfmJ2DMvhhS3sOctMbfXAQ2vnwI; path=/; samesite=none; secure; httponly; partitioned');
});

it('should remove unpartitioned property first with overwrite = true', () => {
const cookies = Cookies({
secure: true,
headers: {
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.3945.29 Safari/537.36',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.3945.29 Safari/537.36',
},
}, { secure: true }, { partitioned: true, removeUnpartitioned: true, overwrite: true });
}, { secure: true }, { partitioned: true, removeUnpartitioned: true, overwrite: true, sameSite: 'none' });
const opts = {
signed: 1,
};
Expand All @@ -554,10 +590,49 @@ describe('test/lib/cookies.test.js', () => {
assert(opts.secure === undefined);
const headers = cookies.ctx.response.headers['set-cookie'];
assert.equal(headers.length, 4);
assert.equal(headers[0], 'foo=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; secure; httponly');
assert.equal(headers[1], 'foo.sig=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; secure; httponly');
assert.equal(headers[2], 'foo=hello; path=/; secure; httponly; partitioned');
assert.equal(headers[3], 'foo.sig=ZWbaA4bWk8ByBuYVgfmJ2DMvhhS3sOctMbfXAQ2vnwI; path=/; secure; httponly; partitioned');
assert.equal(headers[0], 'foo=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; samesite=none; secure; httponly');
assert.equal(headers[1], 'foo.sig=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; samesite=none; secure; httponly');
assert.equal(headers[2], 'foo=hello; path=/; samesite=none; secure; httponly; partitioned');
assert.equal(headers[3], 'foo.sig=ZWbaA4bWk8ByBuYVgfmJ2DMvhhS3sOctMbfXAQ2vnwI; path=/; samesite=none; secure; httponly; partitioned');
});

it('should not set partitioned property when secure = false', () => {
const cookies = Cookies({
secure: true,
headers: {
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.3945.29 Safari/537.36',
},
}, { secure: true }, { partitioned: true, removeUnpartitioned: true, sameSite: 'None' });
const opts = {
signed: 1,
secure: false,
};
cookies.set('foo', 'hello', opts);

assert(opts.signed === 1);
const headers = cookies.ctx.response.headers['set-cookie'];
assert.equal(headers.length, 2);
assert.equal(headers[0], 'foo=hello; path=/; httponly');
assert.equal(headers[1], 'foo.sig=ZWbaA4bWk8ByBuYVgfmJ2DMvhhS3sOctMbfXAQ2vnwI; path=/; httponly');
});

it('should not set partitioned property when sameSite != none', () => {
const cookies = Cookies({
secure: true,
headers: {
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.3945.29 Safari/537.36',
},
}, { secure: true }, { partitioned: true, removeUnpartitioned: true });
const opts = {
signed: 1,
};
cookies.set('foo', 'hello', opts);

assert(opts.signed === 1);
const headers = cookies.ctx.response.headers['set-cookie'];
assert.equal(headers.length, 2);
assert.equal(headers[0], 'foo=hello; path=/; secure; httponly');
assert.equal(headers[1], 'foo.sig=ZWbaA4bWk8ByBuYVgfmJ2DMvhhS3sOctMbfXAQ2vnwI; path=/; secure; httponly');
});
});
});