diff --git a/packages/devextreme/js/aspnet.js b/packages/devextreme/js/aspnet.js index da464425f726..ce8cd815d06d 100644 --- a/packages/devextreme/js/aspnet.js +++ b/packages/devextreme/js/aspnet.js @@ -5,14 +5,15 @@ define(function(require, exports, module) { module.exports = factory( require('jquery'), - require('./core/templates/template_engine_registry').setTemplateEngine, + require('./core/templates/template_engine_registry') + .setTemplateEngine, require('./core/templates/template_base').renderedCallbacks, require('./core/guid'), require('./ui/validation_engine'), require('./core/utils/iterator'), require('./core/utils/dom').extractTemplateMarkup, require('./core/utils/string').encodeHtml, - require('./core/utils/ajax') + require('./core/utils/ajax'), ); }); } else { @@ -25,238 +26,398 @@ DevExpress.utils.iterator, DevExpress.utils.dom.extractTemplateMarkup, DevExpress.utils.string.encodeHtml, - DevExpress.utils.ajax + DevExpress.utils.ajax, ); } -})(function($, setTemplateEngine, templateRendered, Guid, validationEngine, iteratorUtils, extractTemplateMarkup, encodeHtml, ajax) { - var templateCompiler = createTemplateCompiler(); - var pendingCreateComponentRoutines = [ ]; +})( + function( + $, + setTemplateEngine, + templateRendered, + Guid, + validationEngine, + iteratorUtils, + extractTemplateMarkup, + encodeHtml, + ajax, + ) { + var templateCompiler = createTemplateCompiler(); + var pendingCreateComponentRoutines = []; + // csp handling mode marker: detect | once | csp_only | legacy + var cspHandlingMode = setCspHandlingMode(); + var cspRestricted = null; - function createTemplateCompiler() { - var ENCODE_QUALIFIER = '-', - INTERPOLATE_QUALIFIER = '='; + // eslint-disable-next-line no-console + console.log('csp handling mode: ' + cspHandlingMode); - var EXTENDED_OPEN_TAG = /[<[]%/g, - EXTENDED_CLOSE_TAG = /%[>\]]/g; + function setCspHandlingMode() { + const metaCSPMode = window.document.querySelector( + 'meta[name="dx-csp-handling-mode"]', + )?.content; + return metaCSPMode || 'legacy'; + } - function acceptText(bag, text) { - if(text) { - bag.push('_.push(', JSON.stringify(text), ');'); + function readCspDirective(csp, name) { + var directives = csp.split(';'); + for(var i = 0; i < directives.length; i++) { + var parts = directives[i].trim().split(/\s+/); + if(parts.length && parts[0].toLowerCase() === name) { + return parts.slice(1).join(' ').toLowerCase(); + } } + return null; } - function acceptCode(bag, code) { - var encode = code.charAt(0) === ENCODE_QUALIFIER, - value = code.substr(1), - interpolate = code.charAt(0) === INTERPOLATE_QUALIFIER; - - if(encode || interpolate) { - bag.push('_.push('); - var expression = value; - if(encode) { - expression = 'encodeHtml((' + value + ' !== null && ' + value + ' !== undefined) ? ' + value + ' : "")'; - if(/^\s*$/.test(value)) { - expression = 'encodeHtml(' + value + ')'; - } + function canUseFunctionConstructor() { + var isFunctionConstructorAllowed = false; + try { + // eslint-disable-next-line no-new-func + new Function('return true'); + isFunctionConstructorAllowed = true; + } catch(e) { + // Content Security Policy without 'unsafe-eval' blocks the Function constructor. + isFunctionConstructorAllowed = false; + } + return isFunctionConstructorAllowed; + } + + function detectCspRestricted() { + var doc = window.document; + var scripts = doc.getElementsByTagName('script'); + for(var i = 0; i < scripts.length; i++) { + if(scripts[i].nonce || scripts[i].getAttribute('nonce')) { + return true; + } + } + var meta = doc.querySelectorAll( + 'meta[http-equiv=\'Content-Security-Policy\' i]', + ); + for(var j = 0; j < meta.length; j++) { + var content = meta[j].getAttribute('content') || ''; + var directive = readCspDirective(content, 'script-src'); + if(null === directive) { + directive = readCspDirective(content, 'default-src'); + } + if( + null !== directive && + -1 === directive.indexOf('\'unsafe-eval\'') + ) { + return true; } - bag.push(expression); - bag.push(');'); - } else { - bag.push(code + '\n'); } + return false; + } + + function isCspRestricted() { + if(cspRestricted !== null) { + return cspRestricted; + } + switch(cspHandlingMode) { + case 'detect': + cspRestricted = detectCspRestricted(); + break; + case 'csp_only': + cspRestricted = true; + break; + case 'once': + cspRestricted = !canUseFunctionConstructor(); + break; + case 'legacy': + default: + cspRestricted = false; + } + return cspRestricted; + } + + function compileViaScript(src, code) { + if(!src || src.tagName !== 'SCRIPT') { + return null; + } + if(isCspRestricted() && !src.nonce) { + // eslint-disable-next-line no-console + console.warn( + 'DevExtreme: template