From 6034adf8912844cc4dbce1564f091d20ba155b3c Mon Sep 17 00:00:00 2001 From: Mariusz Krzaczkowski Date: Thu, 16 Nov 2023 14:00:48 +0100 Subject: [PATCH] Csrf.min.js --- src/Csrf.min.js | 228 ++++++++++++++++++++++++++++++++++++++++---- src/Csrf.min.js.map | 1 - 2 files changed, 212 insertions(+), 17 deletions(-) delete mode 100644 src/Csrf.min.js.map diff --git a/src/Csrf.min.js b/src/Csrf.min.js index af196d1..c998f9b 100644 --- a/src/Csrf.min.js +++ b/src/Csrf.min.js @@ -1,19 +1,215 @@ -'use strict'; +/** + * @file + * + * Rewrites XMLHttpRequest to automatically send CSRF token with it. In theory + * plays nice with other JavaScript libraries, needs testing though. + */ -function _typeof(o){"@babel/helpers - typeof";return _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(o){return typeof o}:function(o){return o&&"function"==typeof Symbol&&o.constructor===Symbol&&o!==Symbol.prototype?"symbol":typeof o},_typeof(o)}// Sets things up for Mozilla/Opera/nice browsers +// Here are the basic overloaded method definitions +// The wrapper must be set BEFORE onreadystatechange is written to, since +// a bug in ActiveXObject prevents us from properly testing for it. +CsrfMagic = function (real) { + // try to make it ourselves, if you didn't pass it + if (!real) + try { + real = new XMLHttpRequest(); + } catch (e) {} + if (!real) + try { + real = new ActiveXObject('Msxml2.XMLHTTP'); + } catch (e) {} + if (!real) + try { + real = new ActiveXObject('Microsoft.XMLHTTP'); + } catch (e) {} + if (!real) + try { + real = new ActiveXObject('Msxml2.XMLHTTP.4.0'); + } catch (e) {} + this.csrf = real; + // properties + var csrfMagic = this; + real.onreadystatechange = function () { + csrfMagic._updateProps(); + return csrfMagic.onreadystatechange ? csrfMagic.onreadystatechange() : null; + }; + csrfMagic._updateProps(); +}; + +CsrfMagic.prototype = { + open: function (method, url, async, username, password) { + if (method == 'POST') this.csrf_isPost = true; + this.csrf_url = url; + // deal with Opera bug, thanks jQuery + if (username) return this.csrf_open(method, url, async, username, password); + else return this.csrf_open(method, url, async); + }, + csrf_open: function (method, url, async, username, password) { + if (username) return this.csrf.open(method, url, async, username, password); + else return this.csrf.open(method, url, async); + }, + + send: function (data) { + let url = null; + try { + url = new URL(this.csrf_url); + } catch (e) { + //for internal urls there could be no domain part + //in that case it's not a valid url + } + //don't add token to external requests + if (url !== null && typeof csrfMagicDomain !== 'undefined' && url.host !== csrfMagicDomain) { + return this.csrf_send(data); + } + + if (!this.csrf_isPost) return this.csrf_send(data); + delete this.csrf_isPost; + if (data instanceof FormData) { + data.append(csrfMagicName, csrfMagicToken); + return this.csrf_send(data); + } else { + return this.csrf_send(csrfMagicName + '=' + csrfMagicToken + '&' + data); + } + }, + csrf_send: function (data) { + return this.csrf.send(data); + }, + + setRequestHeader: function (header, value) { + // We have to auto-set this at the end, since we don't know how long the + // nonce is when added to the data. + if (this.csrf_isPost && header == 'Content-length') { + this.csrf_purportedLength = value; + return; + } + return this.csrf_setRequestHeader(header, value); + }, + csrf_setRequestHeader: function (header, value) { + return this.csrf.setRequestHeader(header, value); + }, + + abort: function () { + return this.csrf.abort(); + }, + getAllResponseHeaders: function () { + return this.csrf.getAllResponseHeaders(); + }, + getResponseHeader: function (header) { + return this.csrf.getResponseHeader(header); + } // , +}; + +// proprietary +CsrfMagic.prototype._updateProps = function () { + this.readyState = this.csrf.readyState; + if (this.readyState == 4) { + this.responseText = this.csrf.responseText; + this.responseXML = this.csrf.responseXML; + this.status = this.csrf.status; + this.statusText = this.csrf.statusText; + } +}; +CsrfMagic.process = function (base) { + if (typeof base == 'object') { + base[csrfMagicName] = csrfMagicToken; + return base; + } + var prepend = csrfMagicName + '=' + csrfMagicToken; + if (base) return prepend + '&' + base; + return prepend; +}; +// callback function for when everything on the page has loaded +CsrfMagic.end = function () { + // This rewrites forms AGAIN, so in case buffering didn't work this + // certainly will. + forms = document.getElementsByTagName('form'); + for (var i = 0; i < forms.length; i++) { + form = forms[i]; + var method = form.getAttribute('method'); + if (method && method.toUpperCase() !== 'POST') continue; + if (form.elements[csrfMagicName]) continue; + var input = document.createElement('input'); + input.setAttribute('name', csrfMagicName); + input.setAttribute('value', csrfMagicToken); + input.setAttribute('type', 'hidden'); + form.appendChild(input); + } +}; + +// Sets things up for Mozilla/Opera/nice browsers // We very specifically match against Internet Explorer, since they haven't // implemented prototypes correctly yet. -if(CsrfMagic=function CsrfMagic(real){// try to make it ourselves, if you didn't pass it -if(!real)try{real=new XMLHttpRequest;}catch(e){}if(!real)try{real=new ActiveXObject("Msxml2.XMLHTTP");}catch(e){}if(!real)try{real=new ActiveXObject("Microsoft.XMLHTTP");}catch(e){}if(!real)try{real=new ActiveXObject("Msxml2.XMLHTTP.4.0");}catch(e){}this.csrf=real;// properties -var csrfMagic=this;real.onreadystatechange=function(){return csrfMagic._updateProps(),csrfMagic.onreadystatechange?csrfMagic.onreadystatechange():null},csrfMagic._updateProps();},CsrfMagic.prototype={open:function open(method,url,async,username,password){// deal with Opera bug, thanks jQuery -return "POST"==method&&(this.csrf_isPost=!0),this.csrf_url=url,username?this.csrf_open(method,url,async,username,password):this.csrf_open(method,url,async)},csrf_open:function csrf_open(method,url,async,username,password){return username?this.csrf.open(method,url,async,username,password):this.csrf.open(method,url,async)},send:function send(data){var url=null;try{url=new URL(this.csrf_url);}catch(e){//for internal urls there could be no domain part -//in that case it's not a valid url -}//don't add token to external requests -return null!==url&&"undefined"!=typeof csrfMagicDomain&&url.host!==csrfMagicDomain?this.csrf_send(data):this.csrf_isPost?(delete this.csrf_isPost,data instanceof FormData?(data.append(csrfMagicName,csrfMagicToken),this.csrf_send(data)):this.csrf_send(csrfMagicName+"="+csrfMagicToken+"&"+data)):this.csrf_send(data)},csrf_send:function csrf_send(data){return this.csrf.send(data)},setRequestHeader:function setRequestHeader(header,value){// We have to auto-set this at the end, since we don't know how long the -// nonce is when added to the data. -return this.csrf_isPost&&"Content-length"==header?void(this.csrf_purportedLength=value):this.csrf_setRequestHeader(header,value)},csrf_setRequestHeader:function csrf_setRequestHeader(header,value){return this.csrf.setRequestHeader(header,value)},abort:function abort(){return this.csrf.abort()},getAllResponseHeaders:function getAllResponseHeaders(){return this.csrf.getAllResponseHeaders()},getResponseHeader:function getResponseHeader(header){return this.csrf.getResponseHeader(header)}// , -},CsrfMagic.prototype._updateProps=function(){this.readyState=this.csrf.readyState,4==this.readyState&&(this.responseText=this.csrf.responseText,this.responseXML=this.csrf.responseXML,this.status=this.csrf.status,this.statusText=this.csrf.statusText);},CsrfMagic.process=function(base){if("object"==_typeof(base))return base[csrfMagicName]=csrfMagicToken,base;var prepend=csrfMagicName+"="+csrfMagicToken;return base?prepend+"&"+base:prepend},CsrfMagic.end=function(){forms=document.getElementsByTagName("form");for(var i=0;i