should-send-same-site-none
Advanced tools
Comparing version 1.0.1 to 2.0.0
30
index.js
@@ -10,3 +10,3 @@ function intToString(intValue) { | ||
// Donβt send `SameSite=None` to known incompatible clients. | ||
function shouldSendSameSiteNone(useragent) { | ||
function isSameSiteNoneCompactible(useragent) { | ||
return !isSameSiteNoneIncompatible(useragent); | ||
@@ -115,2 +115,28 @@ } | ||
module.exports = shouldSendSameSiteNone; | ||
var shouldSendSameSiteNone = function(req, res, next) { | ||
var end = res.end; | ||
res.end = function() { | ||
var ua = req.get("user-agent"); | ||
var isCompatible = isSameSiteNoneCompactible(ua); | ||
var cookies = res.get("Set-Cookie"); | ||
var removeSameSiteNone = function(str) { | ||
return str.replace(/ SameSite=None;?/g, ""); | ||
}; | ||
if (!isCompatible && cookies) { | ||
if (Array.isArray(cookies)) { | ||
cookies = cookies.map(removeSameSiteNone); | ||
} else { | ||
cookies = removeSameSiteNone(cookies); | ||
} | ||
res.set("Set-Cookie", cookies); | ||
} | ||
end.apply(this, arguments); | ||
}; | ||
next(); | ||
}; | ||
module.exports = { | ||
shouldSendSameSiteNone: shouldSendSameSiteNone, | ||
isSameSiteNoneCompactible: isSameSiteNoneCompactible | ||
}; |
@@ -1,2 +0,8 @@ | ||
const shouldSendSameSiteNone = require("./index"); | ||
const express = require("express"); | ||
const supertest = require("supertest"); | ||
var http = require("http"); | ||
const { | ||
isSameSiteNoneCompactible, | ||
shouldSendSameSiteNone | ||
} = require("./index"); | ||
@@ -76,16 +82,156 @@ const negativeTestCases = { | ||
for (const i in positiveTestCases) { | ||
if (positiveTestCases.hasOwnProperty(i)) { | ||
test(`Test ${i} (true)`, () => { | ||
expect(shouldSendSameSiteNone(positiveTestCases[i])).toBe(true); | ||
describe("isSameSiteNoneCompactible", () => { | ||
for (const i in positiveTestCases) { | ||
if (positiveTestCases.hasOwnProperty(i)) { | ||
it(`Test ${i} (true)`, () => { | ||
expect(isSameSiteNoneCompactible(positiveTestCases[i])).toBe(true); | ||
}); | ||
} | ||
} | ||
for (const i in negativeTestCases) { | ||
if (negativeTestCases.hasOwnProperty(i)) { | ||
it(`Test ${i} (false)`, () => { | ||
expect(isSameSiteNoneCompactible(negativeTestCases[i])).toBe(false); | ||
}); | ||
} | ||
} | ||
}); | ||
describe("shouldSendSameSiteNone with mutiple cookies", () => { | ||
let app, server; | ||
beforeEach(done => { | ||
app = new express(); | ||
app.use(shouldSendSameSiteNone); | ||
app.get("/", (req, res, next) => { | ||
res.cookie("foo", "bar", { sameSite: "none" }); | ||
res.cookie("koo", "mar", { sameSite: "none" }); | ||
res.send("ok"); | ||
}); | ||
server = http.createServer(app); | ||
server.listen(done); | ||
}); | ||
afterEach(done => { | ||
server.close(done); | ||
}); | ||
for (const i in negativeTestCases) { | ||
if (negativeTestCases.hasOwnProperty(i)) { | ||
it(`Remove SameSite=None attributes in ${i}`, async done => { | ||
const response = await supertest(app) | ||
.get("/") | ||
.set("User-Agent", negativeTestCases[i]); | ||
const expected = ["foo=bar; Path=/;,koo=mar; Path=/;"]; | ||
expect(response.header["set-cookie"]).toEqual(expected); | ||
expect(response.text).toEqual("ok"); | ||
done(); | ||
}); | ||
} | ||
} | ||
} | ||
for (const i in negativeTestCases) { | ||
if (negativeTestCases.hasOwnProperty(i)) { | ||
test(`Test ${i} (false)`, () => { | ||
expect(shouldSendSameSiteNone(negativeTestCases[i])).toBe(false); | ||
for (const i in positiveTestCases) { | ||
if (positiveTestCases.hasOwnProperty(i)) { | ||
it(`Keep SameSite=None attributes in ${i}`, async done => { | ||
const response = await supertest(app) | ||
.get("/") | ||
.set("User-Agent", positiveTestCases[i]); | ||
const expected = [ | ||
"foo=bar; Path=/; SameSite=None,koo=mar; Path=/; SameSite=None" | ||
]; | ||
expect(response.header["set-cookie"]).toEqual(expected); | ||
expect(response.text).toEqual("ok"); | ||
done(); | ||
}); | ||
} | ||
} | ||
}); | ||
describe("shouldSendSameSiteNone with single cookies", () => { | ||
let app, server; | ||
beforeEach(done => { | ||
app = new express(); | ||
app.use(shouldSendSameSiteNone); | ||
app.get("/", (req, res, next) => { | ||
res.cookie("foo", "bar", { sameSite: "none" }); | ||
res.send("ok"); | ||
}); | ||
server = http.createServer(app); | ||
server.listen(done); | ||
}); | ||
afterEach(done => { | ||
server.close(done); | ||
}); | ||
for (const i in negativeTestCases) { | ||
if (negativeTestCases.hasOwnProperty(i)) { | ||
it(`Remove SameSite=None attributes in ${i}`, async done => { | ||
const response = await supertest(app) | ||
.get("/") | ||
.set("User-Agent", negativeTestCases[i]); | ||
const expected = ["foo=bar; Path=/;"]; | ||
expect(response.header["set-cookie"]).toEqual(expected); | ||
expect(response.text).toEqual("ok"); | ||
done(); | ||
}); | ||
} | ||
} | ||
} | ||
for (const i in positiveTestCases) { | ||
if (positiveTestCases.hasOwnProperty(i)) { | ||
it(`Keep SameSite=None attributes in ${i}`, async done => { | ||
const response = await supertest(app) | ||
.get("/") | ||
.set("User-Agent", positiveTestCases[i]); | ||
const expected = ["foo=bar; Path=/; SameSite=None"]; | ||
expect(response.header["set-cookie"]).toEqual(expected); | ||
expect(response.text).toEqual("ok"); | ||
done(); | ||
}); | ||
} | ||
} | ||
}); | ||
describe("shouldSendSameSiteNone with no cookies", () => { | ||
let app, server; | ||
beforeEach(done => { | ||
app = new express(); | ||
app.use(shouldSendSameSiteNone); | ||
app.get("/", (req, res, next) => { | ||
res.send("ok"); | ||
}); | ||
server = http.createServer(app); | ||
server.listen(done); | ||
}); | ||
afterEach(done => { | ||
server.close(done); | ||
}); | ||
for (const i in negativeTestCases) { | ||
if (negativeTestCases.hasOwnProperty(i)) { | ||
it(`Remove SameSite=None attributes in ${i}`, async done => { | ||
const response = await supertest(app) | ||
.get("/") | ||
.set("User-Agent", negativeTestCases[i]); | ||
expect(response.header["set-cookie"]).toEqual(undefined); | ||
expect(response.text).toEqual("ok"); | ||
done(); | ||
}); | ||
} | ||
} | ||
for (const i in positiveTestCases) { | ||
if (positiveTestCases.hasOwnProperty(i)) { | ||
it(`Keep SameSite=None attributes in ${i}`, async done => { | ||
const response = await supertest(app) | ||
.get("/") | ||
.set("User-Agent", positiveTestCases[i]); | ||
expect(response.header["set-cookie"]).toEqual(undefined); | ||
expect(response.text).toEqual("ok"); | ||
done(); | ||
}); | ||
} | ||
} | ||
}); |
{ | ||
"name": "should-send-same-site-none", | ||
"version": "1.0.1", | ||
"version": "2.0.0", | ||
"description": "A simple utility to detect incompatible user agents for `SameSite=None` cookie attribute", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "jest" | ||
"test": "jest --detectOpenHandles" | ||
}, | ||
@@ -16,4 +16,6 @@ "repository": { | ||
"devDependencies": { | ||
"jest": "^24.9.0" | ||
"express": "^4.17.1", | ||
"jest": "^24.9.0", | ||
"supertest": "^4.0.2" | ||
} | ||
} |
# should-send-same-site-none | ||
This is a small utility function for detecting incompatible user agents (browsers) for the `SameSite=None` cookie attribute. | ||
The module comes with: | ||
With Chrome 80 in February 2020, [Chrome will treat cookies that have no declared SameSite value as `SameSite=Lax` cookies](https://blog.chromium.org/2019/10/developers-get-ready-for-new.html). Other browser vendors are expected to follow Googleβs lead. | ||
- A small **utility function** `isSameSiteNoneCompactible` for detecting incompatible user agents (browsers) for the `SameSite=None` cookie attribute. | ||
Some browsers, including some versions of Chrome, Safari and UC Browser, might handle the None value in unintended ways, requiring developers to code exceptions for those clients. | ||
- A **Express middleware** `shouldSendSameSiteNone` for automatically removing `SameSite=None` from response header when reqesting client is incompatible with `SameSite=None`. | ||
## Background | ||
`shouldSendSameSiteNone` utility function detect incompatible user agents based on the [list of known incompatible clients](https://www.chromium.org/updates/same-site/incompatible-clients) and returns `true` if the given user-agent string is compatible with `SameSite=None` cookie attribute. | ||
With Chrome 80 in February 2020, Chrome will treat cookies that have no declared SameSite value as `SameSite=Lax` cookies. Other browser vendors are expected to follow Googleβs lead. (See this [Blog Post](https://blog.chromium.org/2019/10/developers-get-ready-for-new.html)). | ||
If you manage cross-site cookies, you will need to apply the SameSite=None; Secure setting to those cookies. However, some browsers, including some versions of Chrome, Safari and UC Browser, might handle the None value in unintended ways, requiring developers to code exceptions for those clients. | ||
`isSameSiteNoneCompactible` utility function detects incompatible user agents based on a [list of known incompatible clients](https://www.chromium.org/updates/same-site/incompatible-clients) and returns `true` if the given user-agent string is compatible with `SameSite=None` cookie attribute. | ||
For Express.js, `shouldSendSameSiteNone` middleware automatically removes `SameSite=None` from set-cookie response header when the reqesting client is incompatible with `SameSite=None`. | ||
## Usage | ||
#### Function: `isSameSiteNoneCompactible` | ||
``` | ||
var shouldSendSameSiteNone = require('should-send-same-site-none'); | ||
// import shouldSendSameSiteNone from 'should-send-same-site-none'; | ||
var ua = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14) AppleWebKit/537.36 (KHTML, like Gecko)'; | ||
import { isSameSiteNoneCompactible } from 'should-send-same-site-none'; | ||
const ua = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14) ....'; | ||
if (shouldSendSameSiteNone(ua)) { | ||
@@ -28,3 +36,20 @@ console.log("Yes, the browser is compatible and we can set SameSite=None cookies"); | ||
#### Middleware: `shouldSendSameSiteNone` | ||
``` | ||
const express = require('express'); | ||
const { shouldSendSameSiteNone } = require('should-send-same-site-none'); | ||
const app = express(); | ||
app.use(shouldSendSameSiteNone); // Apply middle ware before routes | ||
app.get('/', function (req, res) { | ||
res.send('hello world'); | ||
}); | ||
app.listen(3000); | ||
``` | ||
## Running tests | ||
@@ -84,3 +109,2 @@ | ||
Compatibilities of the following clients are unclear: | ||
@@ -91,5 +115,2 @@ | ||
Please file an issue if additional incompatible clients are identified. | ||
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
19881
338
114
3
1