bv-ui-core 2.9.10 → 2.9.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cookie/index.js +23 -13
- package/package.json +1 -3
- package/test/unit/bvFetch/index.spec.js +1 -1
- package/test/unit/cookie/index.spec.js +146 -0
package/lib/cookie/index.js
CHANGED
|
@@ -16,19 +16,29 @@ var store = {};
|
|
|
16
16
|
* @param {Number} days The cookie lifespan in days.
|
|
17
17
|
* @param {String} [domain] The domain for the cookie.
|
|
18
18
|
* @param {Boolean} [secure] Whether this is a secure cookie.
|
|
19
|
+
* @param {String} [sameSite='Lax'] The SameSite attribute ('Strict', 'Lax', or 'None'). Defaults to 'Lax'.
|
|
19
20
|
*/
|
|
20
|
-
function createCookie (name, value, days, domain, secure) {
|
|
21
|
+
function createCookie (name, value, days, domain, secure, sameSite = 'Lax') {
|
|
21
22
|
var date = new Date();
|
|
22
23
|
|
|
23
24
|
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
|
24
25
|
var expires = days ? ';expires=' + date.toGMTString() : '';
|
|
25
26
|
|
|
27
|
+
// Determine if the cookie should be secure.
|
|
28
|
+
// Force the Secure flag if the page is loaded over HTTPS, OR if the explicit `secure` argument is true.
|
|
29
|
+
// This ensures that all cookies (including BVBRANDID) set by this library are secure in production environments.
|
|
30
|
+
var isSecure = secure || (global.location && global.location.protocol === 'https:');
|
|
31
|
+
|
|
32
|
+
// SameSite=None requires the Secure flag, so we fallback to 'Lax' if not secure.
|
|
33
|
+
var sameSiteValue = (sameSite === 'None' && !isSecure) ? 'Lax' : sameSite;
|
|
34
|
+
|
|
26
35
|
var c = encodeURIComponent(name) + '=' +
|
|
27
36
|
encodeURIComponent(value) +
|
|
28
37
|
expires +
|
|
29
38
|
';path=/' +
|
|
30
39
|
(domain ? (';domain=' + domain) : '') +
|
|
31
|
-
(
|
|
40
|
+
(isSecure ? ';secure' : '') +
|
|
41
|
+
';SameSite=' + sameSiteValue;
|
|
32
42
|
|
|
33
43
|
global.document.cookie = c;
|
|
34
44
|
}
|
|
@@ -65,7 +75,7 @@ function readCookie (name) {
|
|
|
65
75
|
function removeCookie (name, domain) {
|
|
66
76
|
delete store[name];
|
|
67
77
|
if (domain) {
|
|
68
|
-
createCookie(name,
|
|
78
|
+
createCookie(name, '', -1, domain);
|
|
69
79
|
}
|
|
70
80
|
else {
|
|
71
81
|
createCookie(name, '', -1);
|
|
@@ -73,23 +83,23 @@ function removeCookie (name, domain) {
|
|
|
73
83
|
}
|
|
74
84
|
|
|
75
85
|
module.exports = {
|
|
76
|
-
create: function (name, value, days, domain, secure) {
|
|
86
|
+
create: function (name, value, days, domain, secure, sameSite) {
|
|
77
87
|
store[name] = value;
|
|
78
88
|
var consentPresent = cookieConsent.getConsent(name);
|
|
79
89
|
if (consentPresent) {
|
|
80
|
-
createCookie(name, value, days, domain, secure);
|
|
90
|
+
createCookie(name, value, days, domain, secure, sameSite);
|
|
81
91
|
}
|
|
82
|
-
cookieConsent.subscribe(name,'add',function (consent) {
|
|
92
|
+
cookieConsent.subscribe(name, 'add', function (consent) {
|
|
83
93
|
if (consent) {
|
|
84
|
-
createCookie(name,value,days,domain,secure);
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
removeCookie(name,domain)
|
|
88
|
-
}
|
|
89
|
-
})
|
|
94
|
+
createCookie(name, value, days, domain, secure, sameSite);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
removeCookie(name, domain);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
90
100
|
|
|
91
101
|
cookieConsent.subscribe(name, 'enable', function () {
|
|
92
|
-
createCookie(name, value, days, domain, secure);
|
|
102
|
+
createCookie(name, value, days, domain, secure, sameSite);
|
|
93
103
|
});
|
|
94
104
|
|
|
95
105
|
cookieConsent.subscribe(name, 'disable', function () {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bv-ui-core",
|
|
3
|
-
"version": "2.9.
|
|
3
|
+
"version": "2.9.11",
|
|
4
4
|
"license": "Apache 2.0",
|
|
5
5
|
"description": "Bazaarvoice UI-related JavaScript",
|
|
6
6
|
"repository": {
|
|
@@ -33,14 +33,12 @@
|
|
|
33
33
|
"karma-firefox-launcher": "^1.1.0",
|
|
34
34
|
"karma-htmlfile-reporter": "^0.3.6",
|
|
35
35
|
"karma-mocha": "^1.3.0",
|
|
36
|
-
"karma-phantomjs-launcher": "^1.0.4",
|
|
37
36
|
"karma-sinon": "^1.0.5",
|
|
38
37
|
"karma-sinon-chai": "^1.3.4",
|
|
39
38
|
"karma-webpack": "^1.7.0",
|
|
40
39
|
"lodash": "^4.17.10",
|
|
41
40
|
"mocha": "^5.2.0",
|
|
42
41
|
"node-libs-browser": "1.0.0",
|
|
43
|
-
"phantomjs": "^2.1.7",
|
|
44
42
|
"sinon": "^4.5.0",
|
|
45
43
|
"sinon-chai": "^2.14.0",
|
|
46
44
|
"webpack": "^1.15.0"
|
|
@@ -43,3 +43,149 @@ describe('lib/cookie', function () {
|
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
});
|
|
46
|
+
|
|
47
|
+
describe('lib/cookie secure flag behavior', function () {
|
|
48
|
+
var originalProtocol;
|
|
49
|
+
|
|
50
|
+
beforeEach(function () {
|
|
51
|
+
// Store original protocol to restore after each test
|
|
52
|
+
originalProtocol = global.location && global.location.protocol;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
afterEach(function () {
|
|
56
|
+
// Restore original protocol
|
|
57
|
+
if (global.location) {
|
|
58
|
+
// Some browsers don't allow direct assignment to location.protocol
|
|
59
|
+
// so we use Object.defineProperty where possible
|
|
60
|
+
try {
|
|
61
|
+
Object.defineProperty(global.location, 'protocol', {
|
|
62
|
+
value: originalProtocol,
|
|
63
|
+
writable: true,
|
|
64
|
+
configurable: true
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
// If we can't restore, tests may need to run in isolation
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Clean up test cookies
|
|
72
|
+
deleteCookie('test%20secure');
|
|
73
|
+
deleteCookie('test%20https');
|
|
74
|
+
deleteCookie('test%20http');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should set secure flag when secure=true is explicitly passed', function () {
|
|
78
|
+
cookieConsent.initConsent({
|
|
79
|
+
'test secure': true
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Create cookie with explicit secure=true
|
|
83
|
+
cookie.create('test secure', 'securevalue', 1, null, true);
|
|
84
|
+
|
|
85
|
+
// Note: We can't directly check if secure flag was set via document.cookie
|
|
86
|
+
// because secure cookies are not visible in document.cookie on http pages.
|
|
87
|
+
// This test verifies the function doesn't throw when secure=true is passed.
|
|
88
|
+
expect(true).to.equal(true);
|
|
89
|
+
|
|
90
|
+
// Clean up
|
|
91
|
+
deleteCookie('test%20secure');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should automatically set secure flag on HTTPS pages', function () {
|
|
95
|
+
// Skip if we can't modify location.protocol
|
|
96
|
+
if (!global.location) {
|
|
97
|
+
this.skip();
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
Object.defineProperty(global.location, 'protocol', {
|
|
103
|
+
value: 'https:',
|
|
104
|
+
writable: true,
|
|
105
|
+
configurable: true
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
catch (e) {
|
|
109
|
+
// Browser doesn't allow protocol modification, skip test
|
|
110
|
+
this.skip();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
cookieConsent.initConsent({
|
|
115
|
+
'test https': true
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Create cookie without explicit secure flag - should auto-secure on HTTPS
|
|
119
|
+
cookie.create('test https', 'httpsvalue', 1);
|
|
120
|
+
|
|
121
|
+
// The cookie creation should succeed without errors
|
|
122
|
+
// On actual HTTPS, the secure flag would be set automatically
|
|
123
|
+
expect(true).to.equal(true);
|
|
124
|
+
|
|
125
|
+
// Clean up
|
|
126
|
+
deleteCookie('test%20https');
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should NOT set secure flag on HTTP pages when secure param is not passed', function () {
|
|
130
|
+
// Skip if we can't modify location.protocol
|
|
131
|
+
if (!global.location) {
|
|
132
|
+
this.skip();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
Object.defineProperty(global.location, 'protocol', {
|
|
138
|
+
value: 'http:',
|
|
139
|
+
writable: true,
|
|
140
|
+
configurable: true
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
catch (e) {
|
|
144
|
+
// Browser doesn't allow protocol modification, skip test
|
|
145
|
+
this.skip();
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
cookieConsent.initConsent({
|
|
150
|
+
'test http': true
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Create cookie without secure flag on HTTP
|
|
154
|
+
cookie.create('test http', 'httpvalue', 1);
|
|
155
|
+
|
|
156
|
+
// Cookie should be readable (not secure, so visible on HTTP)
|
|
157
|
+
expect(global.document.cookie).to.have.string('test%20http=httpvalue');
|
|
158
|
+
|
|
159
|
+
// Clean up
|
|
160
|
+
deleteCookie('test%20http');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('should handle missing global.location gracefully', function () {
|
|
164
|
+
var originalLocation = global.location;
|
|
165
|
+
|
|
166
|
+
// Temporarily remove location
|
|
167
|
+
try {
|
|
168
|
+
delete global.location;
|
|
169
|
+
}
|
|
170
|
+
catch (e) {
|
|
171
|
+
// Can't delete location in some environments, skip
|
|
172
|
+
this.skip();
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
cookieConsent.initConsent({
|
|
177
|
+
'test nolocation': true
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// Should not throw when global.location is undefined
|
|
181
|
+
expect(function () {
|
|
182
|
+
cookie.create('test nolocation', 'value', 1);
|
|
183
|
+
}).to.not.throw();
|
|
184
|
+
|
|
185
|
+
// Restore location
|
|
186
|
+
global.location = originalLocation;
|
|
187
|
+
|
|
188
|
+
// Clean up
|
|
189
|
+
deleteCookie('test%20nolocation');
|
|
190
|
+
});
|
|
191
|
+
});
|