You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by we...@apache.org on 2019/07/19 08:59:59 UTC

[myfaces] branch master updated: https://issues.apache.org/jira/browse/MYFACES-4265, https://issues.apache.org/jira/browse/MYFACES-4280:

This is an automated email from the ASF dual-hosted git repository.

werpu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces.git


The following commit(s) were added to refs/heads/master by this push:
     new 5d12578   https://issues.apache.org/jira/browse/MYFACES-4265,  https://issues.apache.org/jira/browse/MYFACES-4280:
5d12578 is described below

commit 5d125782cd9ec28f531a93cdf674584f155e2d46
Author: Werner Punz <we...@gmail.com>
AuthorDate: Fri Jul 19 10:59:51 2019 +0200

     https://issues.apache.org/jira/browse/MYFACES-4265,
     https://issues.apache.org/jira/browse/MYFACES-4280:
    
     adding nonce support
     Dropping additional legacy code in the eval area and removing the
     now unused runtimequirks file
---
 .../META-INF/resources/myfaces/_impl/_util/_Dom.js |  24 +++-
 .../resources/myfaces/_impl/core/_EvalHandlers.js  | 150 ++++++++-------------
 .../resources/myfaces/_impl/core/_Runtime.js       |  16 ++-
 .../resources/myfaces/_impl/core/_RuntimeQuirks.js |  51 -------
 .../myfaces/_impl/xhrCore/_AjaxResponse.js         |   2 +
 5 files changed, 86 insertions(+), 157 deletions(-)

diff --git a/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_Dom.js b/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_Dom.js
index 4521e59..f3cd731 100644
--- a/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_Dom.js
+++ b/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_Dom.js
@@ -63,6 +63,10 @@ _MF_SINGLTN(_PFX_UTIL + "_Dom", Object, /** @lends myfaces._impl._util._Dom.prot
 
                     newSS.setAttribute("rel", item.getAttribute("rel") || "stylesheet");
                     newSS.setAttribute("type", item.getAttribute("type") || "text/css");
+                    if(item.getAttribute("nonce")) {
+                        newSS.setAttribute("nonce", item.getAttribute("nonce"));
+                    }
+
                     document.getElementsByTagName("head")[0].appendChild(newSS);
                     //ie merrily again goes its own way
                     if (window.attachEvent && !_RT.isOpera && UDEF != typeof newSS.styleSheet && UDEF != newSS.styleSheet.cssText) newSS.styleSheet.cssText = style;
@@ -132,6 +136,8 @@ _MF_SINGLTN(_PFX_UTIL + "_Dom", Object, /** @lends myfaces._impl._util._Dom.prot
                         _Lang.equalsIgnoreCase(type,"text/ecmascript") ||
                         _Lang.equalsIgnoreCase(type,"ecmascript"))) {
 
+                    var nonce = item.getAttribute("nonce") || null;
+
                     var src = item.getAttribute('src');
                     if ('undefined' != typeof src
                             && null != src
@@ -142,14 +148,17 @@ _MF_SINGLTN(_PFX_UTIL + "_Dom", Object, /** @lends myfaces._impl._util._Dom.prot
                         //if jsf.js is already registered we do not replace it anymore
                         if ((src.indexOf("ln=scripts") == -1 && src.indexOf("ln=javax.faces") == -1) || (src.indexOf("/jsf.js") == -1
                                 && src.indexOf("/jsf-uncompressed.js") == -1)) {
+
                             if (finalScripts.length) {
                                 //script source means we have to eval the existing
                                 //scripts before running the include
-                                _RT.globalEval(finalScripts.join("\n"));
+                                for(var cnt = 0; cnt < finalScripts.length; cnt++) {
+                                    _RT.globalEval(finalScripts[cnt].text, finalScripts[cnt]["cspMeta"] || null);
+                                }
 
                                 finalScripts = [];
                             }
-                            _RT.loadScriptEval(src, item.getAttribute('type'), false, "UTF-8", false);
+                            _RT.loadScriptEval(src, item.getAttribute('type'), false, "UTF-8", false, nonce ? {nonce: nonce} : null );
                         }
 
                     } else {
@@ -173,7 +182,12 @@ _MF_SINGLTN(_PFX_UTIL + "_Dom", Object, /** @lends myfaces._impl._util._Dom.prot
                         }
                         // we have to run the script under a global context
                         //we store the script for less calls to eval
-                        finalScripts.push(test);
+                        finalScripts.push(nonce ? {
+                            cspMeta: {nonce: nonce},
+                            text: test
+                        }: {
+                            text: test
+                        });
 
                     }
                 }
@@ -185,7 +199,9 @@ _MF_SINGLTN(_PFX_UTIL + "_Dom", Object, /** @lends myfaces._impl._util._Dom.prot
                 execScrpt(scriptElements[cnt]);
             }
             if (finalScripts.length) {
-                _RT.globalEval(finalScripts.join("\n"));
+                for(var cnt = 0; cnt < finalScripts.length; cnt++) {
+                    _RT.globalEval(finalScripts[cnt].text, finalScripts[cnt]["cspMeta"] || null);
+                }
             }
         } catch (e) {
             //we are now in accordance with the rest of the system of showing errors only in development mode
diff --git a/api/src/main/javascript/META-INF/resources/myfaces/_impl/core/_EvalHandlers.js b/api/src/main/javascript/META-INF/resources/myfaces/_impl/core/_EvalHandlers.js
index e0b9984..f986bf0 100644
--- a/api/src/main/javascript/META-INF/resources/myfaces/_impl/core/_EvalHandlers.js
+++ b/api/src/main/javascript/META-INF/resources/myfaces/_impl/core/_EvalHandlers.js
@@ -70,114 +70,72 @@ if (!myfaces._impl.core._EvalHandlers) {
          */
         var _T = this;
 
-        /*cascaded eval methods depending upon the browser*/
 
         /**
-         * @function
+         * an implementation of eval which drops legacy support
+         * and allows nonce
          * @param code
-
-         *
-         * evals a script globally using exec script (ie6 fallback)
-         * @param {String} code the code which has to be evaluated
-         * @borrows myfaces._impl.core._Runtime as _T
-         *
-         * TODO eval if we cannot replace this method with the head appendix
-         * method which is faster for ie this also would reduce our code
-         * by a few bytes
+         * @param cspMeta optional csp metadata, only allowed key atm nonce
          */
-        _T._evalExecScript = function(code) {
-            //execScript definitely only for IE otherwise we might have a custom
-            //window extension with undefined behavior on our necks
-            //window.execScript does not return anything
-            //on htmlunit it return "null object"
-            //_r == ret
-            var _r = window.execScript(code);
-            if ('undefined' != typeof _r && _r == "null" /*htmlunit bug*/) {
-                return null;
-            }
-            return _r;
-        };
+        _T.globalEval = function(code, cspMeta) {
+            //check for jsf nonce
+            var nonce = cspMeta ? cspMeta.nonce : this._currentScriptNonce();
 
-        /**
-         * flakey head appendix method which does not work in the correct
-         * order or at all for all modern browsers
-         * but seems to be the only method which works on blackberry correctly
-         * hence we are going to use it as fallback
-         *
-         * @param {String} code the code part to be evaled
-         * @borrows myfaces._impl.core._Runtime as _T
-         */
-        _T._evalHeadAppendix = function(code) {
-            //_l == location
-            var _l = document.getElementsByTagName("head")[0] || document.documentElement;
-            //_p == placeHolder
-            var _p = document.createElement("script");
-            _p.type = "text/javascript";
-            _p.text = code;
-            _l.insertBefore(_p, _l.firstChild);
-            _l.removeChild(_p);
-            return null;
+            var element = document.createElement("script");
+            element.setAttribute("type", "text/javascript");
+            element.innerHTML = code;
+            if(nonce) {
+                element.setAttribute("nonce", nonce);
+            }
+            //head appendix method, modern browsers use this method savely to eval scripts
+            //we did not use it up until now because there were really old legacy browsers where
+            //it did not work
+            var htmlScriptElement = document.head.appendChild(element);
+            document.head.removeChild(htmlScriptElement);
         };
 
-        /**
-         * @name myfaces._impl.core._Runtime._standardGlobalEval
-         * @private
-         * @param {String} code
-         */
-        _T._standardGlobalEval = function(code) {
-            //fix which works in a cross browser way
-            //we used to scope an anonymous function
-            //but I think this is better
-            //the reason is some Firefox versions
-            // apply a wrong scope
-            //if we call eval by not scoping
-            //_U == "undefined"
-            var _U = "undefined";
-            var gEval = function () {
-                //_r == retVal;
-                var _r = window.eval.call(window, code);
-                if (_U == typeof _r) return null;
-                return _r;
-            };
-            var _r = gEval();
-            if (_U == typeof _r) return null;
-            return _r;
-        };
+        /*
+        * determines the jsfjs nonce and adds them to the namespace
+        * this is done once and only lazily
+        */
+        _T._currentScriptNonce = function() {
+            //already processed
+            if(myfaces.config && myfaces.config.cspMeta) {
+                return myfaces.config.cspMeta.nonce;
+            }
 
-        /**
-         * global eval on scripts
-         * @param {String} c (code abbreviated since the compression does not work here)
-         * @name myfaces._impl.core._Runtime.globalEval
-         * @function
-         */
-        _T.globalEval = function(c) {
-            //TODO add a config param which allows to evaluate global scripts even if the call
-            //is embedded in an iframe
-            //We lazy init the eval type upon the browsers
-            //capabilities   
-            var _e = "_evalType";
-            var _w = window;
-            var _b = myfaces._impl.core._Runtime.browser;
-            //central routine to determine the eval method
-            if (!_T[_e]) {
-                //execScript supported
-                _T[_e] = _w.execScript ? "_evalExecScript" : null;
+            //since our baseline atm is ie11 we cannot use document.currentScript globally
+            if(document.currentScript && document.currentScript.getAttribute("nonce")) {
+                //fastpath for modern browsers
+                return document.currentScript.getAttribute("nonce") || null;
+            }
 
-                //in case of no support we go to the standard global eval  window.eval.call(window,
-                // with Firefox fixes for scoping
-                _T[_e] = _T[_e] ||(( _w.eval && (!_b.isBlackBerry ||_b.isBlackBerry >= 6)) ? "_standardGlobalEval" : null);
+            var scripts = document.querySelectorAll("script[src], link[src]");
+            var jsf_js = null;
 
-                //this one routes into the hed appendix method
-                _T[_e] = _T[_e] ||((_w.eval ) ? "_evalHeadAppendix" : null);
+            //we search all scripts
+            for(var cnt = 0; scripts && cnt < scripts.length; cnt++) {
+                var scriptNode = scripts[cnt];
+                if(!scriptNode.getAttribute("nonce")) {
+                    continue;
+                }
+                var src = scriptNode.getAttribute("src") || "";
+                if(src && !src.match(/jsf\.js\?ln\=javax\.faces/gi)) {
+                    jsf_js = scriptNode;
+                    //the first one is the one we have our code in
+                    //subsequent ones do not overwrite our code
+                    break;
+                }
             }
-            if (_T[_e]) {
-                //we now execute the eval method
-                return _T[_T[_e]](c);
+            //found
+            myfaces.config = myfaces.config || {};
+            myfaces.config.cspMeta = myfaces.config.cspMeta || {
+                nonce: null
+            };
+            if(jsf_js) {
+                myfaces.config.cspMeta.nonce = jsf_js.getAttribute("nonce") || null;
             }
-            //we probably have covered all browsers, but this is a safety net which might be triggered
-            //by some foreign browser which is not covered by the above cases
-            eval.call(window, c);
-            return null;
+            return myfaces.config.cspMeta.nonce;
         };
 
     };
diff --git a/api/src/main/javascript/META-INF/resources/myfaces/_impl/core/_Runtime.js b/api/src/main/javascript/META-INF/resources/myfaces/_impl/core/_Runtime.js
index 6b4cf1e..5e859d8 100644
--- a/api/src/main/javascript/META-INF/resources/myfaces/_impl/core/_Runtime.js
+++ b/api/src/main/javascript/META-INF/resources/myfaces/_impl/core/_Runtime.js
@@ -73,8 +73,8 @@ if (!myfaces._impl.core._Runtime) {
          * @name myfaces._impl.core._Runtime.globalEval
          * @function
          */
-        _T.globalEval = function(code) {
-            return myfaces._impl.core._EvalHandlers.globalEval(code);
+        _T.globalEval = function(code, cspMeta) {
+            return myfaces._impl.core._EvalHandlers.globalEval(code, cspMeta);
         };
 
         /**
@@ -391,9 +391,10 @@ if (!myfaces._impl.core._Runtime) {
          * @param {Boolean} defer  defer true or false, same as the javascript tag defer param
          * @param {String} charSet the charset under which the script has to be loaded
          * @param {Boolean} async tells whether the script can be asynchronously loaded or not, currently
+         * @param cspMetas csp meta data to be processed by globalEval
          * not used
          */
-        this.loadScriptEval = function(src, type, defer, charSet, async) {
+        this.loadScriptEval = function(src, type, defer, charSet, async, cspMeta) {
             var xhr = _T.getXHRObject();
             xhr.open("GET", src, false);
 
@@ -415,12 +416,12 @@ if (!myfaces._impl.core._Runtime) {
                         //we moved the sourceurl notation to # instead of @ because ie does not cover it correctly
                         //newer browsers understand # including ie since windows 8.1
                         //see http://updates.html5rocks.com/2013/06/sourceMappingURL-and-sourceURL-syntax-changed
-                        _T.globalEval(xhr.responseText.replace("\n", "\r\n") + "\r\n//# sourceURL=" + src);
+                        _T.globalEval(xhr.responseText.replace("\n", "\r\n") + "\r\n//# sourceURL=" + src, cspMeta);
                     } else {
                         //TODO not ideal we maybe ought to move to something else here
                         //but since it is not in use yet, it is ok
                         setTimeout(function() {
-                            _T.globalEval(xhr.responseText.replace("\n", "\r\n") + "\r\n//# sourceURL=" + src);
+                            _T.globalEval(xhr.responseText.replace("\n", "\r\n") + "\r\n//# sourceURL=" + src, cspMeta);
                         }, 1);
                     }
                 } else {
@@ -441,7 +442,7 @@ if (!myfaces._impl.core._Runtime) {
          * @param {Boolean} defer  defer true or false, same as the javascript tag defer param
          * @param {String} charSet the charset under which the script has to be loaded
          */
-        this.loadScriptByBrowser = function(src, type, defer, charSet, async) {
+        this.loadScriptByBrowser = function(src, type, defer, charSet, async, cspMeta) {
             //if a head is already present then it is safer to simply
             //use the body, some browsers prevent head alterations
             //after the first initial rendering
@@ -461,6 +462,9 @@ if (!myfaces._impl.core._Runtime) {
 
                 script.type = type || "text/javascript";
                 script.src = src;
+                if(cspMeta && cspMeta.nonce) {
+                    script.setAttribute("nonce", cspMeta.nonce);
+                }
                 if (charSet) {
                     script.charset = charSet;
                 }
diff --git a/api/src/main/javascript/META-INF/resources/myfaces/_impl/core/_RuntimeQuirks.js b/api/src/main/javascript/META-INF/resources/myfaces/_impl/core/_RuntimeQuirks.js
deleted file mode 100644
index b5bdb3d..0000000
--- a/api/src/main/javascript/META-INF/resources/myfaces/_impl/core/_RuntimeQuirks.js
+++ /dev/null
@@ -1,51 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-if (!document.querySelectorAll || !window.XMLHttpRequest) {
-
-    //initial browser detection, we encapsule it in a closure
-    //to drop all temporary variables from ram as soon as possible
-    //we run into the quirks fallback if XMLHttpRequest is not enabled
-    (function() {
-        var _T  = myfaces._impl.core._Runtime;
-
-        _T.getXHRObject = function() {
-            //since this is a global object ie hates it if we do not check for undefined
-            if (window.XMLHttpRequest) {
-                var _ret = new XMLHttpRequest();
-                //we now check the xhr level
-                //sendAsBinary = 1.5 which means mozilla only
-                //upload attribute present == level2
-
-                if (!_T.XHR_LEVEL) {
-                    var _e = _T.exists;
-                    _T.XHR_LEVEL = (_e(_ret, "sendAsBinary")) ? 1.5 : 1;
-                    _T.XHR_LEVEL = (_e(_ret, "upload") && 'undefined' != typeof FormData) ? 2 : _T.XHR_LEVEL;
-                }
-                return _ret;
-            }
-            //IE
-            try {
-                _T.XHR_LEVEL = 1;
-                return new ActiveXObject("Msxml2.XMLHTTP");
-            } catch (e) {
-
-            }
-            return new ActiveXObject('Microsoft.XMLHTTP');
-        };
-
-
-    })();
-}
\ No newline at end of file
diff --git a/api/src/main/javascript/META-INF/resources/myfaces/_impl/xhrCore/_AjaxResponse.js b/api/src/main/javascript/META-INF/resources/myfaces/_impl/xhrCore/_AjaxResponse.js
index 6e2285d..2fb3bc7 100644
--- a/api/src/main/javascript/META-INF/resources/myfaces/_impl/xhrCore/_AjaxResponse.js
+++ b/api/src/main/javascript/META-INF/resources/myfaces/_impl/xhrCore/_AjaxResponse.js
@@ -456,6 +456,8 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
                 case this.CMD_UPDATE:
                     this.processUpdate(request, context, changes[i]);
                     break;
+                //this one needs a csp spec extension for the global eval
+                //for now we recycle the csp for this case from the jsf.js file
                 case this.CMD_EVAL:
                     _Lang.globalEval(changes[i].firstChild.data);
                     break;