webpack-hot-client icon indicating copy to clipboard operation
webpack-hot-client copied to clipboard

Bundled code includes `eval()`, requiring a relaxed CSP

Open walkerburgin opened this issue 6 years ago • 2 comments

  • Operating System: OSX
  • Node Version: 8.11.3
  • NPM Version: [email protected]
  • webpack Version: 4.9.2
  • webpack-hot-client Version: 4.1.1

Expected Behavior

Webpack hot client should be able to work without requiring an 'unsafe-eval' CSP.

Actual Behavior

I think that requiring the "dist" version of loglevelnext like this results in bundling code with eval()'d modules:

https://github.com/webpack-contrib/webpack-hot-client/blob/0ac06733c7eafdd8f1a89dea2c2df9000de9a70b/lib/client/log.js#L2

Snippet from the resulting bundle:

/***/ "./lib/LogLevel.js":
/*!*************************!*\
  !*** ./lib/LogLevel.js ***!
  \*************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
eval("\n/* global window: true */\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nvar PrefixFactory = __webpack_require__(/*! ../factory/PrefixFactory */ \"./factory/PrefixFactory.js\");\n\nvar MethodFactory = __webpack_require__(/*! ./MethodFactory */ \"./lib/MethodFactory.js\");\n\nvar defaults = {\n  factory: null,\n  level: 'warn',\n  name: +new Date(),\n  prefix: null\n};\n\nmodule.exports =\n/*#__PURE__*/\nfunction () {\n  function LogLevel(options) {\n    _classCallCheck(this, LogLevel);\n\n    // implement for some _very_ loose type checking. avoids getting into a\n    // circular require between MethodFactory and LogLevel\n    this.type = 'LogLevel';\n    this.options = Object.assign({}, defaults, options);\n    this.methodFactory = options.factory;\n\n    if (!this.methodFactory) {\n      var factory = options.prefix ? new PrefixFactory(this, options.prefix) : new MethodFactory(this);\n      this.methodFactory = factory;\n    }\n\n    if (!this.methodFactory.logger) {\n      this.methodFactory.logger = this;\n    }\n\n    this.name = options.name || '<unknown>'; // this.level is a setter, do this after setting up the factory\n\n    this.level = this.options.level;\n  }\n\n  _createClass(LogLevel, [{\n    key: \"disable\",\n    value: function disable() {\n      this.level = this.levels.SILENT;\n    }\n  }, {\n    key: \"enable\",\n    value: function enable() {\n      this.level = this.levels.TRACE;\n    }\n  }, {\n    key: \"factory\",\n    get: function get() {\n      return this.methodFactory;\n    },\n    set: function set(factory) {\n      factory.logger = this;\n      this.methodFactory = factory;\n      this.methodFactory.replaceMethods(this.level);\n    }\n  }, {\n    key: \"level\",\n    get: function get() {\n      return this.currentLevel;\n    },\n    set: function set(logLevel) {\n      var level = this.methodFactory.distillLevel(logLevel);\n\n      if (level == null) {\n        throw new Error(\"loglevelnext: setLevel() called with invalid level: \".concat(logLevel));\n      }\n\n      this.currentLevel = level;\n      this.methodFactory.replaceMethods(level);\n\n      if (typeof console === 'undefined' && level < this.levels.SILENT) {\n        // eslint-disable-next-line no-console\n        console.warn('loglevelnext: console is undefined. The log will produce no output.');\n      }\n    }\n  }, {\n    key: \"levels\",\n    get: function get() {\n      // eslint-disable-line class-methods-use-this\n      return this.methodFactory.levels;\n    }\n  }]);\n\n  return LogLevel;\n}();\n\n//# sourceURL=webpack://log/./lib/LogLevel.js?");

/***/ }),

I'm not sure off the top of my head why the dist specifically is being require'd instead of require("loglevelnext") (which I don't think would have this problem).

I know it's probably a little unusual to set a CSP like this in development, but it reflects the way that the code is run in production.

How Do We Reproduce?

Set a CSP that prohibits 'unsafe-eval'.

walkerburgin avatar Aug 30 '18 16:08 walkerburgin

There's https://github.com/webpack-contrib/webpack-log which might just supersede the direct use of loglevelnext in this project regardless.

greglo avatar Aug 30 '18 18:08 greglo

Friendly ping -- is this something you'd be open to a PR for?

walkerburgin avatar Sep 04 '18 17:09 walkerburgin