Skip to content

Use of eval and Content Security Policy #695

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
iquito opened this issue Feb 13, 2020 · 5 comments · Fixed by #697
Closed

Use of eval and Content Security Policy #695

iquito opened this issue Feb 13, 2020 · 5 comments · Fixed by #697
Labels

Comments

@iquito
Copy link

iquito commented Feb 13, 2020

I am using Webpack Encore for a very minimalistic usage of JS and CSS and noticed the JS when using dev-server (encore dev-server) contains eval everywhere, therefore it doesn't work with any reasonable Content Security Policy, where eval is the first and most important thing to be prohibited.

It does work fine with encore production, and one could just not use any Content Security Policy for development, yet that may lead to breaks in production because the CSP was not tested / realistically set in development, so ideally no eval is ever used anywhere. I managed to solve the usage of eval by changing the value of devtool like shown in my configuration below:

var Encore = require('@symfony/webpack-encore');

Encore
    .setOutputPath('public/assets/')
    .setPublicPath('/assets')
    .addEntry('app', './assets/js/app.js')
    .splitEntryChunks()
    .enableSingleRuntimeChunk()
    .cleanupOutputBeforeBuild()
    .enableVersioning(Encore.isProduction())
;

process.env.NODE_ENV = process.env.NODE_ENV || 'development';

Encore.configureWatchOptions(watchOptions => {
    watchOptions.poll = 250; // check for changes every 250 ms
});

encoreConfig = Encore.getWebpackConfig();

// This prevents the use of eval and makes
// webpack in development work with a Content-Security-Policy
encoreConfig.devtool = "source-map";

module.exports = encoreConfig;

I found the solution in a webpack issue about the usage of eval in webpack, although there were multiple solutions there depending on what kind of packages people used, so finding that solution was a bit random.

I thought I would start a discussion here about making Webpack Encore work with a reasonable Content Security Policy by default, so the friction to implement a CSP becomes lower, and/or to add documentation about how one can configure Encore to work without eval and with a CSP. I am also not sure if my above approach is the only way of removing eval, or if devtool can be set through the Encore object. In general I think it would be nice to factor in the usage of CSP, and I could help with documentation if needed.

@Lyrkan
Copy link
Collaborator

Lyrkan commented Feb 13, 2020

Hi @iquito,

That's weird, devtool should be set to source-map in production or inline-source-map in dev:

if (this.webpackConfig.useSourceMaps) {
if (this.webpackConfig.isProduction()) {
// https://webpack.js.org/configuration/devtool/#for-production
config.devtool = 'source-map';
} else {
// https://webpack.js.org/configuration/devtool/#for-development
config.devtool = 'inline-source-map';
}
}

And none of these values are supposed to use eval(...) as you can see on the following page: https://github.com/webpack/webpack/tree/webpack-4/examples/source-map

You probably didn't notice it on a small project but using source-map is way slower than the other values (inline-source-map already being considered slow), which prevent us from using it by default for dev builds: https://webpack.js.org/configuration/devtool/#devtool

As a matter of fact there was some discussion in #214 about switching to eval-* source maps, but that didn't go through since it caused other issues (including CSP concerns).

If that's really a problem it could probably be documented and we could also make it easier to change that option through Encore.enableSourceMaps() (see #214 (comment)).

@iquito
Copy link
Author

iquito commented Feb 13, 2020

Changing it to inline-source-map works too for me, no evals or CSP errors then - but not setting it at all leads to this kind of code in vendors-app.js (this is just the beginning, the whole file is huge):

(window["webpackJsonp"] = window["webpackJsonp"] || []).push([["vendors~app"],{

/***/ "./node_modules/ansi-html/index.js":
/*!*****************************************!*\
  !*** ./node_modules/ansi-html/index.js ***!
  \*****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
eval("\n\nmodule.exports = ansiHTML\n\n// Reference to https://github.com/sindresorhus/ansi-regex\nvar _regANSI = /(?:(?:\\u001b\\[)|\\u009b)(?:(?:[0-9]{1,3})?(?:(?:;[0-9]{0,3})*)?[A-M|f-m])|\\u001b[A-M]/\n\nvar _defColors = {\n  reset: ['fff', '000'], // [FOREGROUD_COLOR, BACKGROUND_COLOR]\n  black: '000',\n  red: 'ff0000',\n  green: '209805',\n  yellow: 'e8bf03',\n  blue: '0000ff',\n  magenta: 'ff00ff',\n  cyan: '00ffee',\n  lightgrey: 'f0f0f0',\n  darkgrey: '888'\n}\nvar _styles = {\n  30: 'black',\n  31: 'red',\n  32: 'green',\n  33: 'yellow',\n  34: 'blue',\n  35: 'magenta',\n  36: 'cyan',\n  37: 'lightgrey'\n}\nvar _openTags = {\n  '1': 'font-weight:bold', // bold\n  '2': 'opacity:0.5', // dim\n  '3': '<i>', // italic\n  '4': '<u>', // underscore\n  '8': 'display:none', // hidden\n  '9': '<del>' // delete\n}\nvar _closeTags = {\n  '23': '</i>', // reset italic\n  '24': '</u>', // reset underscore\n  '29': '</del>' // reset delete\n}\n\n;[0, 21, 22, 27, 28, 39, 49].forEach(function (n) {\n  _closeTags[n] = '</span>'\n})\n\n/**\n * Converts text with ANSI color codes to HTML markup.\n * @param {String} text\n * @returns {*}\n */\nfunction ansiHTML (text) {\n  // Returns the text if the string has no ANSI escape code.\n  if (!_regANSI.test(text)) {\n    return text\n  }\n\n  // Cache opened sequence.\n  var ansiCodes = []\n  // Replace with markup.\n  var ret = text.replace(/\\033\\[(\\d+)*m/g, function (match, seq) {\n    var ot = _openTags[seq]\n    if (ot) {\n      // If current sequence has been opened, close it.\n      if (!!~ansiCodes.indexOf(seq)) { // eslint-disable-line no-extra-boolean-cast\n        ansiCodes.pop()\n        return '</span>'\n      }\n      // Open tag.\n      ansiCodes.push(seq)\n      return ot[0] === '<' ? ot : '<span style=\"' + ot + ';\">'\n    }\n\n    var ct = _closeTags[seq]\n    if (ct) {\n      // Pop sequence\n      ansiCodes.pop()\n      return ct\n    }\n    return ''\n  })\n\n  // Make sure tags are closed.\n  var l = ansiCodes.length\n  ;(l > 0) && (ret += Array(l + 1).join('</span>'))\n\n  return ret\n}\n\n/**\n * Customize colors.\n * @param {Object} colors reference to _defColors\n */\nansiHTML.setColors = function (colors) {\n  if (typeof colors !== 'object') {\n    throw new Error('`colors` parameter must be an Object.')\n  }\n\n  var _finalColors = {}\n  for (var key in _defColors) {\n    var hex = colors.hasOwnProperty(key) ? colors[key] : null\n    if (!hex) {\n      _finalColors[key] = _defColors[key]\n      continue\n    }\n    if ('reset' === key) {\n      if (typeof hex === 'string') {\n        hex = [hex]\n      }\n      if (!Array.isArray(hex) || hex.length === 0 || hex.some(function (h) {\n        return typeof h !== 'string'\n      })) {\n        throw new Error('The value of `' + key + '` property must be an Array and each item could only be a hex string, e.g.: FF0000')\n      }\n      var defHexColor = _defColors[key]\n      if (!hex[0]) {\n        hex[0] = defHexColor[0]\n      }\n      if (hex.length === 1 || !hex[1]) {\n        hex = [hex[0]]\n        hex.push(defHexColor[1])\n      }\n\n      hex = hex.slice(0, 2)\n    } else if (typeof hex !== 'string') {\n      throw new Error('The value of `' + key + '` property must be a hex string, e.g.: FF0000')\n    }\n    _finalColors[key] = hex\n  }\n  _setTags(_finalColors)\n}\n\n/**\n * Reset colors.\n */\nansiHTML.reset = function () {\n  _setTags(_defColors)\n}\n\n/**\n * Expose tags, including open and close.\n * @type {Object}\n */\nansiHTML.tags = {}\n\nif (Object.defineProperty) {\n  Object.defineProperty(ansiHTML.tags, 'open', {\n    get: function () { return _openTags }\n  })\n  Object.defineProperty(ansiHTML.tags, 'close', {\n    get: function () { return _closeTags }\n  })\n} else {\n  ansiHTML.tags.open = _openTags\n  ansiHTML.tags.close = _closeTags\n}\n\nfunction _setTags (colors) {\n  // reset all\n  _openTags['0'] = 'font-weight:normal;opacity:1;color:#' + colors.reset[0] + ';background:#' + colors.reset[1]\n  // inverse\n  _openTags['7'] = 'color:#' + colors.reset[1] + ';background:#' + colors.reset[0]\n  // dark grey\n  _openTags['90'] = 'color:#' + colors.darkgrey\n\n  for (var code in _styles) {\n    var color = _styles[code]\n    var oriColor = colors[color] || '000'\n    _openTags[code] = 'color:#' + oriColor\n    code = parseInt(code)\n    _openTags[(code + 10).toString()] = 'background:#' + oriColor\n  }\n}\n\nansiHTML.reset()\n\n\n//# sourceURL=webpack:///./node_modules/ansi-html/index.js?");

/***/ }),

If I add Encore.enableSourceMaps(!Encore.isProduction()) to my config, then it works, I just assumed if I did not enable source maps, they would be disabled, but they seem to be set to eval-source-maps or something similar instead.

@Lyrkan
Copy link
Collaborator

Lyrkan commented Feb 13, 2020

I think you found a bug!

It seems that not setting devtool (which is what Encore does when you call enableSourceMaps(false) or don't call that function at all) does not have the same meaning when you are in dev or prod: the former will use eval while the latter will use (none).

We should probably force devtool = false by default.

@iquito
Copy link
Author

iquito commented Feb 15, 2020

Yep, devtool = false is what I expected, as I did not want to enable source maps and therefore just removed the enableSourceMaps call.

@r7developer1
Copy link

still having the same problem even after the setting the source maps to false.
i am using react , react-dom , redux , redux-tool-kit, redux-sagas with web-pack encore.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants