Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
983 views
in Technique[技术] by (71.8m points)

content security policy - Tricks or overrides to make ExtJS application strict CSP compatible

when the server sends a restrictive Content-Security-Policy header,

Content-Security-Policy: default-src 'self'; script-src 'self'; img-src 'self'

the following error comes up in Chrome :

Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self'".

Strict Content-Security-Policy does not allow eval-like mechanisms, unless the 'unsafe-eval' keyword is specified.

Do you have any tricks or overrides to make your ExtJS application strict CSP compatible ?

The first culprit is the following code, where a Function object is being created:

 getInstantiator: function(length) {
        var instantiators = this.instantiators,
            instantiator,
            i,
            args;

        instantiator = instantiators[length];

        if (!instantiator) {
            i = length;
            args = [];

            for (i = 0; i < length; i++) {
                args.push('a[' + i + ']');
            }

            instantiator = instantiators[length] = new Function('c', 'a', 'return new c(' + args.join(',') + ')'); //// CSP PB HERE
           //<debug>
            instantiator.name = "Ext.create" + length;
            //</debug>
        }

        return instantiator;
    },

"new Function" is used also here :

 makeInitializeFn: function (cls) {
            var code = ['var '],
                body = ['
return function (e) {
    var data = e.data, v;
'],
                work = 0,
                bc, ec, // == beginClone, endClone
                convert, expr, factory, field, fields, fs, hasDefValue, i, length;

            if (!(fields = cls.rankedFields)) {
                // On the first edit of a record of this type we need to ensure we have the
                // topo-sort done:
                fields = cls.rankFields();
            }

            for (i = 0, length = fields.length; i < length; ++i) {
                // The generated method declares vars for each field using "f0".."fN' as the
                // name. These are used to access properties of the field (e.g., the convert
                // method or defaultValue).
                field = fields[i];
                fs = 'f' + i;
                convert = field.convert;

                if (i) {
                    code.push(',  
    ');
                }
                code.push(fs, ' = $fields[' + i + ']');
                //<debug>
                // this can be helpful when debugging (at least in Chrome):
                code.push('  /*  ', field.name, '  */');
                //</debug>

                // NOTE: added string literals are "folded" by the compiler so we
                // are better off doing an "'foo' + 'bar'" then "'foo', 'bar'". But
                // for variables we are better off pushing them into the array for
                // the final join.

                if ((hasDefValue = (field.defaultValue !== undefined)) || convert) {
                    // For non-calculated fields that have some work required (a convert method
                    // and/or defaultValue), generate a chunk of logic appropriate for the
                    // field.
                    //expr = data["fieldName"];
                    expr = 'data["' + field.name + '"]';
                    ++work;

                    bc = ec = '';
                    if (field.cloneDefaultValue) {
                        bc = 'Ext.clone(';
                        ec = ')';
                    }

                    body.push('
');
                    if (convert && hasDefValue) {
                        // v = data.fieldName;
                        // if (v !== undefined) {
                        //     v = f2.convert(v, e);
                        // }
                        // if (v === undefined) {
                        //     v = f2.defaultValue;
                        //      // or
                        //     v = Ext.clone(f2.defaultValue);
                        // }
                        // data.fieldName = v;
                        //
                        body.push('    v = ', expr, ';
' +
                                  '    if (v !== undefined) {
' +
                                  '        v = ', fs, '.convert(v, e);
' +
                                  '    }
' +
                                  '    if (v === undefined) {
' +
                                  '        v = ', bc, fs, '.defaultValue',ec,';
' +
                                  '    }
' +
                                  '    ', expr, ' = v;');
                    } else if (convert) { // no defaultValue
                        // v = f2.convert(data.fieldName,e);
                        // if (v !== undefined) {
                        //     data.fieldName = v;
                        // }
                        //
                        body.push('    v = ', fs, '.convert(', expr, ',e);
' +
                                  '    if (v !== undefined) {
' +
                                  '        ', expr, ' = v;
' +
                                  '    }
');
                    } else if (hasDefValue) { // no convert
                        // if (data.fieldName === undefined) {
                        //     data.fieldName = f2.defaultValue;
                        //          // or
                        //     data.fieldName = Ext.clone(f2.defaultValue);
                        // }
                        //
                        body.push('    if (', expr, ' === undefined) {
' +
                                  '        ', expr, ' = ',bc,fs,'.defaultValue',ec,';
' +
                                  '    }
');
                    }
                }
            }

            if (!work) {
                // There are no fields that need special processing
                return Ext.emptyFn;
            }

            code.push(';
');
            code.push.apply(code, body);
            code.push('}');
            code = code.join('');

            // Ensure that Ext in the function code refers to the same Ext that we are using here.
            // If we are in a sandbox, global.Ext might be different.
            factory = new Function('$fields', 'Ext', code); /// CSP PROBLEM HERE

            return factory(fields, Ext);
        }
    } // static
} // privates

},

This policy prevents new Function(), which rely upon for a performance optimisation in ExtJS, I suppose.

The policy prevents also the use of "eval".

Ext.JSON = (new(function() {
// @define Ext.JSON
// @require Ext
// @require Ext.Error

var me = this,
    hasNative = window.JSON && JSON.toString() === '[object JSON]',
    useHasOwn = !! {}.hasOwnProperty,
    pad = function(n) {
        return n < 10 ? "0" + n : n;
    },
    doDecode = function(json) {
        return eval("(" + json + ')'); // jshint ignore:line //////CSP PROBLEM
    },

...
// in Template.js
    evalCompiled: function($) {

        // We have to use eval to realize the code block and capture the inner func we also
        // don't want a deep scope chain. We only do this in Firefox and it is also unhappy
        // with eval containing a return statement, so instead we assign to "$" and return
        // that. Because we use "eval", we are automatically sandboxed properly.
        eval($); // jshint ignore:line
        return $;
    },

//in XTemplateCompiler
    evalTpl: function ($) {

        // We have to use eval to realize the code block and capture the inner func we also
        // don't want a deep scope chain. We only do this in Firefox and it is also unhappy
        // with eval containing a return statement, so instead we assign to "$" and return
        // that. Because we use "eval", we are automatically sandboxed properly.
        eval($);
        return $;
    },

// in Managet.js
    privates: {
        addProviderClass: function(type, cls) {
            this.providerClasses[type] = cls;
        },
        
        onApiLoadSuccess: function(options) {
            var me = this,
                url = options.url,
                varName = options.varName,
                api, provider, error;
            
            try {
                // Variable name could be nested (default is Ext.REMOTING_API),
                // so we use eval() to get the actual value.
                api = Ext.apply(options.config, eval(varName)); ////////CSP
                
                provider = me.addProvider(api);
            }

// in Ext.dom.Query
    eval("var batch = 30803, child, next, prev, byClassName;");
//...

    eval(fn.join(""));
question from:https://stackoverflow.com/questions/65829801/tricks-or-overrides-to-make-extjs-application-strict-csp-compatible

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)
Waitting for answers

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...