1/* 2 * The OWASP CSRFGuard Project, BSD License 3 * Copyright (c) 2011, Eric Sheridan ([email protected]) 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of OWASP nor the names of its contributors may be used 15 * to endorse or promote products derived from this software without specific 16 * prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30/** 31 * Issue 92: boolean check to avoid running the function multiple times. 32 * Happens if the file is included multiple times which results in 33 * Maximum call stack size exceeded 34 */ 35var owaspCSRFGuardScriptHasLoaded = owaspCSRFGuardScriptHasLoaded || {}; 36if (owaspCSRFGuardScriptHasLoaded !== true) { 37 (function () { 38 owaspCSRFGuardScriptHasLoaded = true; 39 40 /** 41 * Code to ensure our event always gets triggered when the DOM is updated. 42 * @param obj 43 * @param type 44 * @param fn 45 * @source http://www.dustindiaz.com/rock-solid-addevent/ 46 */ 47 function addEvent(obj, type, fn) { 48 if (obj.addEventListener) { 49 obj.addEventListener(type, fn, false); 50 EventCache.add(obj, type, fn); 51 } else if (obj.attachEvent) { 52 obj['e' + type + fn] = fn; 53 obj[type + fn] = function () { 54 obj['e' + type + fn](window.event); 55 }; 56 obj.attachEvent('on' + type, obj[type + fn]); 57 EventCache.add(obj, type, fn); 58 } else { 59 obj['on' + type] = obj['e' + type + fn]; 60 } 61 } 62 63 var EventCache = function () { 64 var listEvents = []; 65 return { 66 listEvents: listEvents, 67 add: function (node, sEventName, fHandler) { 68 listEvents.push(arguments); 69 }, 70 flush: function () { 71 var i, item; 72 for (i = listEvents.length - 1; i >= 0; i = i - 1) { 73 item = listEvents[i]; 74 if (item[0].removeEventListener) { 75 item[0].removeEventListener(item[1], item[2], item[3]); 76 } 77 78 if (item[1].substring(0, 2) !== 'on') { 79 item[1] = 'on' + item[1]; 80 } 81 82 if (item[0].detachEvent) { 83 item[0].detachEvent(item[1], item[2]); 84 } 85 } 86 } 87 }; 88 }(); 89 90 /* string utility functions */ 91 function startsWith(s, prefix) { 92 return s.indexOf(prefix) === 0; 93 } 94 95 function endsWith(s, suffix) { 96 return s.substring(s.length - suffix.length) === suffix; 97 } 98 99 /** 100 * hook using standards based prototype 101 */ 102 function hijackStandard() { 103 XMLHttpRequest.prototype._open = XMLHttpRequest.prototype.open; 104 XMLHttpRequest.prototype.open = function (method, url, async, user, pass) { 105 this.url = url; 106 107 this._open.apply(this, arguments); 108 }; 109 110 XMLHttpRequest.prototype._send = XMLHttpRequest.prototype.send; 111 XMLHttpRequest.prototype.send = function (data) { 112 if (this.onsend !== null) { 113 this.onsend.apply(this, arguments); 114 } 115 116 this._send.apply(this, arguments); 117 }; 118 } 119 120 /** 121 * ie does not properly support prototype - wrap completely 122 */ 123 function hijackExplorer() { 124 var xmlHttpRequest = window.XMLHttpRequest; 125 126 function allocXMLHttpRequest() { 127 this.base = xmlHttpRequest ? new xmlHttpRequest : new window.ActiveXObject('Microsoft.XMLHTTP'); 128 } 129 130 function initXMLHttpRequest() { 131 return new allocXMLHttpRequest; 132 } 133 134 initXMLHttpRequest.prototype = allocXMLHttpRequest.prototype; 135 136 /* constants */ 137 initXMLHttpRequest.UNSENT = 0; 138 initXMLHttpRequest.OPENED = 1; 139 initXMLHttpRequest.HEADERS_RECEIVED = 2; 140 initXMLHttpRequest.LOADING = 3; 141 initXMLHttpRequest.DONE = 4; 142 143 /* properties */ 144 initXMLHttpRequest.prototype.status = 0; 145 initXMLHttpRequest.prototype.statusText = ''; 146 initXMLHttpRequest.prototype.readyState = initXMLHttpRequest.UNSENT; 147 initXMLHttpRequest.prototype.responseText = ''; 148 initXMLHttpRequest.prototype.responseXML = null; 149 initXMLHttpRequest.prototype.onsend = null; 150 151 initXMLHttpRequest.url = null; 152 initXMLHttpRequest.onreadystatechange = null; 153 154 /* methods */ 155 initXMLHttpRequest.prototype.open = function (method, url, async, user, pass) { 156 var self = this; 157 this.url = url; 158 159 this.base.onreadystatechange = function () { 160 try { 161 self.status = self.base.status; 162 } catch (e) { 163 } 164 try { 165 self.statusText = self.base.statusText; 166 } catch (e) { 167 } 168 try { 169 self.readyState = self.base.readyState; 170 } catch (e) { 171 } 172 try { 173 self.responseText = self.base.responseText; 174 } catch (e) { 175 } 176 try { 177 self.responseXML = self.base.responseXML; 178 } catch (e) { 179 } 180 181 if (self.onreadystatechange !== null) { 182 self.onreadystatechange.apply(this, arguments); 183 } 184 }; 185 186 this.base.open(method, url, async, user, pass); 187 }; 188 189 initXMLHttpRequest.prototype.send = function (data) { 190 if (this.onsend !== null) { 191 this.onsend.apply(this, arguments); 192 } 193 194 this.base.send(data); 195 }; 196 197 initXMLHttpRequest.prototype.abort = function () { 198 this.base.abort(); 199 }; 200 201 initXMLHttpRequest.prototype.getAllResponseHeaders = function () { 202 return this.base.getAllResponseHeaders(); 203 }; 204 205 initXMLHttpRequest.prototype.getResponseHeader = function (name) { 206 return this.base.getResponseHeader(name); 207 }; 208 209 initXMLHttpRequest.prototype.setRequestHeader = function (name, value) { 210 return this.base.setRequestHeader(name, value); 211 }; 212 213 /* hook */ 214 window.XMLHttpRequest = initXMLHttpRequest; 215 } 216 217 /** 218 * check if valid domain based on domainStrict 219 */ 220 function isValidDomain(current, target) { 221 var result = false;
222 223 /* check exact or subdomain match */ 224 if (current === target) { 225 result = true; 226 } else if (true === false) { 227 if (target.charAt(0) === '.') { 228 result = endsWith(current, target); 229 } else { 230 result = endsWith(current, '.' + target); 231 } 232 } 233 234 return result; 235 } 236 237 /** 238 * determine if uri/url points to valid domain 239 */ 240 function isValidUrl(src) { 241 var result = false; 242 var urlStartsWithProtocol = /^[a-zA-Z][a-zA-Z0-9.+-]*:/; 243 244 /* parse out domain to make sure it points to our own */ 245 if (src.substring(0, 7) === 'http://' || src.substring(0, 8) === 'https://') { 246 var token = '://'; 247 var index = src.indexOf(token); 248 var part = src.substring(index + token.length); 249 var domain = ''; 250 251 /* parse up to end, first slash, or anchor */ 252 for (var i = 0; i < part.length; i++) { 253 var character = part.charAt(i); 254 if (character === '/' || character === ':' || character === '#') { 255 break; 256 } else { 257 domain += character; 258 } 259 } 260 261 result = isValidDomain(document.domain, domain); 262 /* explicitly skip anchors */ 263 } else if (src.charAt(0) === '#') { 264 result = false;
265 /* ensure it is a local resource without a protocol */ 266 } else if (!startsWith(src, '//') && (src.charAt(0) === '/' || src.search(urlStartsWithProtocol) === -1)) { 267 result = true; 268 } 269 270 return result; 271 } 272 273 /* parse uri from url */ 274 function parseUri(url) { 275 var uri = ''; 276 var token = '://'; 277 var index = url.indexOf(token); 278 var part = ''; 279 280 /* 281 * ensure to skip protocol and prepend context path for non-qualified 282 * resources (ex: 'protect.html' vs 283 * '/Owasp.CsrfGuard.Test/protect.html'). 284 */ 285 if (index > 0) { 286 part = url.substring(index + token.length); 287 } else if (url.charAt(0) !== '/') { 288 part = '/jw/' + url; 289 } else { 290 part = url; 291 } 292 293 /* parse up to end or query string */ 294 var uriContext = (index === -1); 295 296 for (var i = 0; i < part.length; i++) { 297 var character = part.charAt(i); 298 299 if (character === '/') { 300 uriContext = true; 301 } else if (uriContext === true && (character === '?' || character === '#')) { 302 uriContext = false; 303 break; 304 } 305 306 if (uriContext === true) { 307 uri += character; 308 } 309 } 310 311 return uri; 312 } 313 314 function calculatePageTokenForUri(pageTokens, uri) { 315 let value = null; 316 Object.keys(pageTokens).forEach(function (pageTokenKey) { 317 var pageToken = pageTokens[pageTokenKey]; 318 319 if (uri === pageTokenKey) { 320 value = pageToken; 321 } else if (pageTokenKey.startsWith('^') && pageTokenKey.endsWith('$')) { // regex matching 322 if (new RegExp(pageTokenKey).test(uri)) { 323 value = pageToken; 324 } 325 } else if (pageTokenKey.startsWith('/*')) { // full path wildcard path matching 326 value = pageToken; 327 } else if (pageTokenKey.endsWith('/*') || pageTokenKey.startsWith('.*')) { // 'partial path wildcard' and 'extension' matching 328 // TODO implement 329 console.warn("'Extension' and 'partial path wildcard' matching for page tokens is not supported properly yet! " + 330 "Every resource will be assigned a new unique token instead of using the defined resource matcher token. " + 331 "Although this is not a security issue, in case of a large REST application it can have an impact on performance." + 332 "Consider using regular expressions instead."); 333 } 334 }); 335 return value; 336 } 337 338 /** 339 * inject tokens as hidden fields into forms 340 */ 341 function injectTokenForm(form, tokenName, tokenValue, pageTokens, injectGetForms) { 342 343 if (!injectGetForms) { 344 var method = form.getAttribute('method'); 345 346 if ((typeof method !== 'undefined') && method !== null && method.toLowerCase() === 'get') { 347 return; 348 } 349 } 350 351 var value = tokenValue; 352 var action = form.getAttribute('action'); 353 354 if (action !== null && isValidUrl(action)) { 355 var uri = parseUri(action); 356 const calculatedPageToken = calculatePageTokenForUri(pageTokens, uri); 357 value = calculatedPageToken == null ? tokenValue : calculatedPageToken; 358 359 let hiddenTokenFields = Object.keys(form.elements).filter(function (i) { 360 return form.elements[i].name === tokenName; 361 }); 362 363 if (hiddenTokenFields.length === 0) { 364 var hidden = document.createElement('input'); 365 366 hidden.setAttribute('type', 'hidden'); 367 hidden.setAttribute('name', tokenName); 368 hidden.setAttribute('value', value); 369 370 form.appendChild(hidden); 371 console.debug('Hidden input element [', hidden, '] was added to the form: ', form); 372 } else { 373 hiddenTokenFields.forEach(function (i) { 374 return form.elements[i].value = value; 375 }); 376 console.debug('Hidden token fields [', hiddenTokenFields, '] of form [', form, '] were updated with new token value: ', value); 377 } 378 } 379 } 380 381 /** 382 * inject tokens as query string parameters into url 383 */ 384 function injectTokenAttribute(element, attr, tokenName, tokenValue, pageTokens) { 385 386 const addTokenToLocation = function(location, tokenName, value) { 387 let newLocation; 388 if (location.indexOf('?') === -1) { 389 newLocation = location + '?' + tokenName + '=' + value; 390 } else { 391 newLocation = location + '&' + tokenName + '=' + value; 392 } 393 return newLocation; 394 } 395 396 const location = element.getAttribute && element.getAttribute(attr); 397 398 if (location != null && isValidUrl(location) && !isUnprotectedExtension(location)) { 399 const uri = parseUri(location); 400 const calculatedPageToken = calculatePageTokenForUri(pageTokens, uri); 401 const value = calculatedPageToken == null ? tokenValue : calculatedPageToken; 402 403 const tokenValueMatcher = new RegExp('(?:' + tokenName + '=)([^?|#|&]+)', 'g
403i'); 404 const tokenMatches = tokenValueMatcher.exec(location); 405 406 if (tokenMatches === null || tokenMatches.length === 0) { 407 let newLocation; 408 409 const anchorIndex = location.indexOf('#'); 410 if (anchorIndex !== -1) { 411 const baseLocation = location.split('#')[0]; 412 const anchor = location.substring(anchorIndex); 413 414 newLocation = addTokenToLocation(baseLocation, tokenName, value) + anchor; 415 } else { 416 newLocation = addTokenToLocation(location, tokenName, value); 417 } 418 419 try { 420 element.setAttribute(attr, newLocation); 421 console.debug('Attribute [', attr, '] with value [', newLocation, '] set for element: ', element); 422 } catch (e) { 423 // attempted to set/update unsupported attribute 424 } 425 } else { 426 let newLocation = location; 427 tokenMatches.slice(1).forEach(function (match) { 428 return newLocation = newLocation.replace(match, value); 429 }); 430 431 element.setAttribute(attr, newLocation); 432 console.debug('Attribute [', attr, '] with value [', newLocation, '] set for element: ', element); 433 } 434 } 435 } 436 437 /** 438 * Added to support isUnprotectedExtension(src) 439 * @param filename 440 * @return extension or EMPTY 441 */ 442 function getFileExtension(filename) { 443 var extension = ''; 444 /* take the part before the ';' if it exists (often for UrlRewriting - ex: ;JSESSIONID=x) */ 445 if (filename.indexOf(';') !== -1) { 446 filename = filename.split(';')[0]; 447 } 448 449 if (filename.indexOf('.') !== -1) { 450 extension = filename.substring(filename.lastIndexOf('.') + 1, filename.length) || filename; 451 } 452 return extension; 453 } 454 455 /** 456 * get the file extension and match it against a list of known static file extensions 457 * @param src 458 * @return 459 */ 460 function isUnprotectedExtension(src) { 461 var isSupported = false; 462 var exts = '';/* example(for properties): 'js,css,gif,png,ico,jpg' */ 463 if (exts !== '') { 464 var filename = parseUri(src); 465 var ext = getFileExtension(filename).toLowerCase(); 466 var e = exts.split(','); 467 for (var i = 0; i < e.length; i++) { 468 if (e[i] === ext) { 469 isSupported = true; 470 break; 471 } 472 } 473 } 474 return isSupported; 475 } 476 477 function injectToElements(domElements, tokenName, tokenValue, pageTokens) { 478 var len = domElements.length; 479 480 var injectForms = true; 481 var injectGetForms = false; 482 var injectFormAttributes = false; 483 var injectAttributes = false; 484 485 for (let i = 0; i < len; i++) { 486 let element = domElements[i]; 487 488 if (element.tagName && element.tagName.toLowerCase() === 'form') { 489 if (injectForms) { 490 injectTokenForm(element, tokenName, tokenValue, pageTokens, injectGetForms); 491 492 /* adjust array length after addition of new element */ 493 len = domElements.length; // TODO review 494 } 495 if (injectFormAttributes) { 496 injectTokenAttribute(element, 'action', tokenName, tokenValue, pageTokens); 497 } 498 /* inject into attribute */ 499 } else if (injectAttributes) { 500 injectTokenAttribute(element, 'src', tokenName, tokenValue, pageTokens); 501 injectTokenAttribute(element, 'href', tokenName, tokenValue, pageTokens); 502 } 503 } 504 } 505 506 /** 507 * inject csrf prevention tokens throughout dom 508 */ 509 function injectTokens(tokenName, tokenValue, existingPageTokens) { 510 /* obtain reference to page tokens if enabled */ 511 var pageTokens = {}; 512 513 if (false) { 514 pageTokens = existingPageTokens; 515 } 516 517 /* iterate over all elements and injection token */ 518 var all = document.all ? document.all : document.getElementsByTagName('*'); 519 520 injectToElements(all, tokenName, tokenValue, pageTokens); 521 } 522 523 /** 524 * obtain array of page specific tokens 525 */ 526 function requestPageTokens(tokenName, tokenValue, callback) { 527 const xhr = window.XMLHttpRequest ? new window.XMLHttpRequest : new window.ActiveXObject('Microsoft.XMLHTTP'); 528 529 xhr.open('POST', '/jw/csrf', true); 530 531 /* if AJAX is enabled, the token header will be automatically added, no need to set it again */ 532 if (true !== true) { 533 if (tokenName !== undefined && tokenValue !== undefined) { 534 xhr.setRequestHeader(tokenName, tokenValue); 535 } 536 } 537 538 xhr.onreadystatechange = function () { 539 if (xhr.readyState === 4) { 540 if (xhr.status === 200) { 541 let pageTokens = JSON.parse(xhr.responseText)['pageTokens']; 542 console.debug('Received page tokens: ', pageTokens); 543 callback.call(this, pageTokens); 544 } else { 545 alert(xhr.status + ': CSRF check failed'); 546 } 547 } 548 }; 549 550 xhr.send(null); 551 } 552 553 function handleDynamicallyCreatedNodes() { 554 const dynamicNodeCreationEventName = ''; 555 556 if (dynamicNodeCreationEventName && dynamicNodeCreationEventName.length > 0) { 557 addEvent(window, dynamicNodeCreationEventName, function (event) { 558 injectToElements([event.detail], tokenName, masterTokenValue, pageTokenWrapper.pageTokens); 559 }); 560 } else { 561 if (MutationObserver) { 562 const formMutationObserver = new MutationObserver(function (mutations, observer) { 563 for (let i in mutations) { 564 const mutation = mutations[i]; 565 const addedNodes = mutation.addedNodes; 566 if (mutation.type === 'childList' && addedNodes.length && addedNodes.length > 0) { 567 injectToElements(addedNodes, tokenName, masterTokenValue, pageTokenWrapper.pageTokens); 568 } 569 } 570 }); 571 572 formMutationObserver.observe(document, {attributes: false, childList: true, subtree: true}); 573 addEvent(window, 'unload', formMutationObserver.disconnect); 574 } else { 575 addEvent(window, 'DOMNodeInserted', function (event) { 576 const target = event.target || event.srcElement; 577 if (event.type === 'DOMNodeInserted') { 578 injectToElements([target], tokenName, masterTokenValue, pageTokenWrapper.pageTokens); 579 } 580 }); 581 } 582 } 583 } 584 585 /* 586 * Only inject the tokens if the JavaScript was referenced from HTML that 587 * was served by us. Otherwise, the code was referenced from malicious HTML 588 * which may be trying to steal tokens using JavaScript hijacking techniques. 589 * The token is now removed and fetched using another POST request to solve, 590 * the token hijacking problem. 591 */ 592 if (isValidDomain(document.domain, 'demarches.vincennes.fr')) { 593 var tokenName = 'OWASP_CSRFTOKEN'; 594 var masterTokenValue = ''; 595 console.debug('Master token [' + tokenName + ']: ', masterTokenValue); 596 597 var isLoadedWrapper = {isDomContentLoaded: false}; 598 599 var pageTokenWrapper = {pageTokens: {}}; 600 601 addEvent(window, 'unload', EventCache.flush); 602 603 addEvent(window, 'DOMContentLoaded', function () { 604 isLoadedWrapper.isDomContentLoaded = true; 605 606 if (pageTokenWrapper.pageTokensLoaded) { 607 injectTokens(tokenName, masterTokenValue, pageTokenWrapper.pageTokens); 608 } 609 }); 610 611 if (false) { // TODO should it be invoked only after the DOMContentLoaded? 612 handleDynamicallyCreatedNodes(); 613 } 614 615 /* optionally include Ajax support */ 616 if (true) { 617 if (navigator.appName === 'Microsoft Internet Explorer') { 618 hijackExplorer(); 619 } else { 620 hijackStandard(); 621 } 622 623 /* CUSTOM START : keep track on headers set */ 624 if (XMLHttpRequest.prototype._setRequestHeader === undefined) { 625 XMLHttpRequest.prototype._setRequestHeader = XMLHttpRequest.prototype.setRequestHeader; 626 } 627 XMLHttpRequest.prototype.setRequestHeader = function(key, value) { 628 if (this._headers === undefined) { 629 this._headers = {}; 630 } 631 this._headers[key] = value; 632 this._setRequestHeader(key, value); 633 }; 634 /* CUSTOM END */ 635 636 XMLHttpRequest.prototype.onsend = function (data) { 637 /* CUSTOM START : It is handled with connection manager 638 639 addEvent(this, 'readystatechange', function () { 640 if (this.readyState === 4) { 641 let tokenResponseHeader = this.getResponseHeader(tokenName); 642 if (tokenResponseHeader != undefined) { 643 try { 644 let tokenTO = JSON.parse(tokenResponseHeader) 645 646 let newMasterToken = tokenTO['masterToken']; 647 if (newMasterToken !== undefined) { 648 masterTokenValue = newMasterToken; 649 console.debug('New master token value received: ', masterTokenValue); 650 } 651 652 let newPageTokens = tokenTO['pageTokens']; 653 if (newPageTokens !== undefined) { 654 Object.keys(newPageTokens).forEac
654h(function (key) { 655 return pageTokenWrapper.pageTokens[key] = newPageTokens[key]; 656 }); 657 console.debug('New page token value(s) received: ', newPageTokens); 658 } 659 660 injectTokens(tokenName, masterTokenValue, pageTokenWrapper.pageTokens); 661 } catch (e) { 662 console.error("Error while updating tokens from response header.") 663 } 664 } 665 } 666 }); 667 668 CUSTOM END */ 669 670 var computePageToken = function(pageTokens, modifiedUri) { 671 let result = null; 672 673 let pathWithoutLeadingSlash = window.location.pathname.substring(1); // e.g. deploymentName/service/endpoint 674 let pathArray = pathWithoutLeadingSlash.split('/'); 675 676 let builtPath = ''; 677 for (let i = 0; i < pathArray.length - 1; i++) { // the last part of the URI (endpoint) is disregarded because the modifiedUri parameter is used instead 678 builtPath += '/' + pathArray[i]; 679 let pageTokenValue = calculatePageTokenForUri(pageTokens, builtPath + modifiedUri); 680 if (pageTokenValue != undefined) { 681 result = pageTokenValue; 682 break; 683 } 684 } 685 686 return result; 687 }; 688 689 /** 690 * For the library to function correctly, all the URLs must start with a forward slash (/) 691 * Parameters must be removed from the URL 692 */ 693 var normalizeUrl = function(url) { 694 var removeParameters = function(currentUrl, symbol) { 695 let index = currentUrl.indexOf(symbol); 696 return index > 0 ? currentUrl.substring(0, index) : currentUrl; 697 } 698 699 /* 700 * TODO should other checks be done here like in the isValidUrl? 701 * Could the url parameter contain full URLs with protocol domain, port etc? 702 */ 703 let normalizedUrl = url.startsWith('/') ? url : '/' + url; 704 705 normalizedUrl = removeParameters(normalizedUrl, '?'); 706 normalizedUrl = removeParameters(normalizedUrl, '#'); 707 708 return normalizedUrl; 709 } 710 711 if (isValidUrl(this.url)) { 712 this.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); 713 /* CUSTOM START : Handle by connection manager */ 714// let normalizedUrl = normalizeUrl(this.url); 715// 716// if (pageTokenWrapper.pageTokens === null) { 717// this.setRequestHeader(tokenName, masterTokenValue); 718// } else { 719// 720// let pageToken = calculatePageTokenForUri(pageTokenWrapper.pageTokens, normalizedUrl); 721// if (pageToken == undefined) { 722// let computedPageToken = computePageToken(pageTokenWrapper.pageTokens, normalizedUrl); 723// 724// if (computedPageToken === null) { 725// this.setRequestHeader(tokenName, masterTokenValue); 726// } else { 727// this.setRequestHeader(tokenName, computedPageToken); 728// } 729// } else { 730// this.setRequestHeader(tokenName, pageToken); 731// } 732 if (this._headers[ConnectionManager.tokenName] === undefined) { 733 this.setRequestHeader(ConnectionManager.tokenName, ConnectionManager.tokenValue); 734 } 735 736// } 737 /* CUSTOM END */ 738 } 739 }; 740 } 741 742 if (false) { 743 let pageTokenRequestCallback = function (receivedPageTokens) { 744 pageTokenWrapper.pageTokens = receivedPageTokens; 745 746 pageTokenWrapper.pageTokensLoaded = true; 747 748 if (isLoadedWrapper.isDomContentLoaded) { 749 injectTokens(tokenName, masterTokenValue, receivedPageTokens); 750 } 751 }; 752 753 requestPageTokens(tokenName, masterTokenValue, pageTokenRequestCallback); 754 } else { 755 /* update nodes in DOM after load */ 756 addEvent(window, 'DOMContentLoaded', function () { 757 /* CUSTOM START */ 758 injectTokens(ConnectionManager.tokenName, ConnectionManager.tokenValue, {}); 759 //injectTokens(tokenName, masterTokenValue, {}); 760 /* CUSTOM END */ 761 }); 762 } 763 } else { 764 alert('OWASP CSRFGuard JavaScript was included from within an unauthorized domain!'); 765 } 766 })(); 767}
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.