PageSourceSearch

https://demarches.vincennes.fr/jw/csrf

js vincennes.fr collected 2026-10-02 05:58:11 UTC 32,437 bytes, 767 lines download raw bytes

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.