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.
@@ -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
- (secure ? (';secure') : '');
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, null, -1, domain);
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.10",
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"
@@ -66,7 +66,7 @@ describe('BvFetch', function () {
66
66
  statusText: 'OK',
67
67
  headers: {
68
68
  'Cache-Control': 'max-age=3600',
69
- 'X-Cached-Time': Date.now()
69
+ 'X-Bazaarvoice-Cached-Time': Date.now()
70
70
  }
71
71
  });
72
72
 
@@ -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
+ });