1/** 2 * jquerysession.js â jQuery session plugin with sessionStorage + cookie fallback 3 * 4 * Security patch (VAPT finding: Header Manipulation â Cookies, Medium): 5 * All direct document.cookie writes replaced with CookieService.set() / 6 * CookieService.remove() calls. This enforces: 7 * ⢠Value sanitisation (allow-list / strip of dangerous chars) before write. 8 * ⢠Secure attribute on every cookie (prevents transmission over plain HTTP). 9 * ⢠SameSite=Strict on every cookie (CSRF defence). 10 * 11 * Dependency inversion: this module now depends on the CookieService 12 * abstraction rather than writing to document.cookie directly, keeping 13 * security policy centralised and unit-testable in one place. 14 * 15 * Prerequisite: cookie-service.js must be loaded before this file. 16 * 17 * @requires window.CookieService (public/js/cookie-service.js) 18 * @security VAPT-remediation â Header Manipulation: Cookies (Medium) 19 */ 20(function($){ 21 22 // Guard: fail loudly at initialisation time rather than silently 23 // falling back to bare document.cookie writes. 24 if (typeof window.CookieService === 'undefined') { 25 throw new Error( 26 '[jquerysession] CookieService is not loaded. ' + 27 'Ensure cookie-service.js is included before jquerysession.js.' 28 ); 29 } 30 31 var _cs = window.CookieService; 32 33 // Register the strict allow-list rule for the protocol-marker cookie. 34 // This cookie stores only 'http:' or 'https:' â any other value is blocked. 35 _cs.addRule('_jqs_protocol', { 36 pattern: /^https?:$/, 37 maxLength: 6 38 }); 39 40 $.session = { 41 42 _id: null, 43 44 _cookieCache: undefined, 45 46 _init: function() 47 { 48 if (!window.name) { 49 window.name = Math.random(); 50 } 51 this._id = window.name; 52 this._initCache(); 53 54 // See if we've changed protocols 55 var protocolCookie = _cs.get(this._generatePrefix()); 56 if (protocolCookie && document.location.protocol !== protocolCookie) { 57 this._clearSession(); 58 for (var key in this._cookieCache) { 59 try { 60 window.sessionStorage.setItem(key, this._cookieCache[key]); 61 } catch (e) {} 62 } 63 } 64 65 // Write the protocol marker cookie (value is always http: or https:, 66 // which passes the _jqs_protocol strict allow-list in CookieService). 67 _cs.set( 68 this._generatePrefix(), 69 document.location.protocol, 70 { expires: new Date((new Date).getTime() + 120000) } 71 ); 72 }, 73 74 _generatePrefix: function() 75 { 76 return '__session:' + this._id + ':'; 77 }, 78 79 _initCache: function() 80 { 81 var cookies = document.cookie.split(';'); 82 this._cookieCache = {}; 83 for (var i in cookies) { 84 var kv = cookies[i].trim().split('='); 85 var decodedKey; 86 try { decodedKey = decodeURIComponent(kv[0]); } catch(e) { continue; } 87 if ((new RegExp(this._generatePrefix() + '.+')).test(decodedKey) && kv[1]) { 88 this._cookieCache[decodedKey.split(':', 3)[2]] = decodeURIComponent(kv[1]); 89 } 90 } 91 }, 92 93 _setFallback: function(key, value, onceOnly) 94 { 95 var cookieName = this._generatePrefix() + key; 96 var opts = { path: '/' }; 97 if (onceOnly) { 98 opts.expires = new Date(Date.now() + 120000); 99 } 100 // CookieService.set() validates/sanitises the value and applies 101 // Secure + SameSite=Strict before touching document.cookie. 102 _cs.set(cookieName, value, opts); 103 this._cookieCache[key] = value; 104 return this; 105 }, 106 107 _getFallback: function(key) 108 { 109 if (!this._cookieCache) { 110 this._initCache(); 111 } 112 return this._cookieCache[key]; 113 }, 114 115 _clearFallback: function() 116 { 117 for (var i in this._cookieCache) { 118 _cs.remove(this._generatePrefix() + i); 119 } 120 this._cookieCache = {}; 121 }, 122 123 _deleteFallback: function(key) 124 {
vendor: 1,263 bytes, lines 125-178
125 _cs.remove(this._generatePrefix() + key); 126 delete this._cookieCache[key]; 127 }, 128 129 get: function(key) 130 { 131 return window.sessionStorage.getItem(key) || this._getFallback(key); 132 }, 133 134 set: function(key, value, onceOnly) 135 { 136 try { 137 window.sessionStorage.setItem(key, value); 138 } catch (e) {} 139 this._setFallback(key, value, onceOnly || false); 140 return this; 141 }, 142 143 'delete': function(key){ 144 return this.remove(key); 145 }, 146 147 remove: function(key) 148 { 149 try { 150 window.sessionStorage.removeItem(key); 151 } catch (e) {} 152 this._deleteFallback(key); 153 return this; 154 }, 155 156 _clearSession: function() 157 { 158 try { 159 window.sessionStorage.clear(); 160 } catch (e) { 161 for (var i in window.sessionStorage) { 162 window.sessionStorage.removeItem(i); 163 } 164 } 165 }, 166 167 clear: function() 168 { 169 this._clearSession(); 170 this._clearFallback(); 171 return this; 172 } 173 174 }; 175 176 $.session._init(); 177 178})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.