forked from linsight/should-send-same-site-none
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
140 lines (118 loc) · 4.01 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
function intToString(intValue) {
return String(intValue);
}
function stringToInt(strValue) {
return parseInt(strValue, 10) || 0;
}
// Don’t send `SameSite=None` to known incompatible clients.
function isSameSiteNoneCompatible(useragent) {
return !isSameSiteNoneIncompatible(String(useragent));
}
// Classes of browsers known to be incompatible.
function isSameSiteNoneIncompatible(useragent) {
return (
hasWebKitSameSiteBug(useragent) ||
dropsUnrecognizedSameSiteCookies(useragent)
);
}
function hasWebKitSameSiteBug(useragent) {
return (
isIosVersion(12, useragent) ||
(isMacosxVersion(10, 14, useragent) &&
(isSafari(useragent) || isMacEmbeddedBrowser(useragent)))
);
}
function dropsUnrecognizedSameSiteCookies(useragent) {
return (
(isChromiumBased(useragent) &&
isChromiumVersionAtLeast(51, useragent) &&
!isChromiumVersionAtLeast(67, useragent)) ||
(isUcBrowser(useragent) && !isUcBrowserVersionAtLeast(12, 13, 2, useragent))
);
}
// Regex parsing of User-Agent string.
function regexContains(stringValue, regex) {
var matches = stringValue.match(regex);
return matches !== null;
}
function extractRegexMatch(stringValue, regex, offsetIndex) {
var matches = stringValue.match(regex);
if (matches !== null && matches[offsetIndex] !== undefined) {
return matches[offsetIndex];
}
return null;
}
function isIosVersion(major, useragent) {
var regex = /\(iP.+; CPU .*OS (\d+)[_\d]*.*\) AppleWebKit\//;
// Extract digits from first capturing group.
return extractRegexMatch(useragent, regex, 1) === intToString(major);
}
function isMacosxVersion(major, minor, useragent) {
var regex = /\(Macintosh;.*Mac OS X (\d+)_(\d+)[_\d]*.*\) AppleWebKit\//;
// Extract digits from first and second capturing groups.
return (
extractRegexMatch(useragent, regex, 1) === intToString(major) &&
extractRegexMatch(useragent, regex, 2) === intToString(minor)
);
}
function isSafari(useragent) {
var safari_regex = /Version\/.* Safari\//;
return useragent.match(safari_regex) !== null && !isChromiumBased(useragent);
}
function isMacEmbeddedBrowser(useragent) {
var regex = /^Mozilla\/[\.\d]+ \(Macintosh;.*Mac OS X [_\d]+\) AppleWebKit\/[\.\d]+ \(KHTML, like Gecko\)$/;
return regexContains(useragent, regex);
}
function isChromiumBased(useragent) {
const regex = /Chrom(e|ium)/;
return regexContains(useragent, regex);
}
function isChromiumVersionAtLeast(major, useragent) {
var regex = /Chrom[^ \/]+\/(\d+)[\.\d]* /;
// Extract digits from first capturing group.
var version = stringToInt(extractRegexMatch(useragent, regex, 1));
return version >= major;
}
function isUcBrowser(useragent) {
var regex = /UCBrowser\//;
return regexContains(useragent, regex);
}
function isUcBrowserVersionAtLeast(major, minor, build, useragent) {
var regex = /UCBrowser\/(\d+)\.(\d+)\.(\d+)[\.\d]* /;
// Extract digits from three capturing groups.
var major_version = stringToInt(extractRegexMatch(useragent, regex, 1));
var minor_version = stringToInt(extractRegexMatch(useragent, regex, 2));
var build_version = stringToInt(extractRegexMatch(useragent, regex, 3));
if (major_version !== major) {
return major_version > major;
}
if (minor_version != minor) {
return minor_version > minor;
}
return build_version >= build;
}
var shouldSendSameSiteNone = function(req, res, next) {
var end = res.end;
res.end = function() {
var ua = req.get("user-agent");
var isCompatible = isSameSiteNoneCompatible(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,
isSameSiteNoneCompatible: isSameSiteNoneCompatible
};