How to remove eval and Function constructor from webpack build to avoid CSP issues - google-chrome-extension

Problem is with Webpack which uses eval in compiled code. Due to this, Chrome extension and Firefox addons does not work as it requires 'unsafe-eval' directive in CSP property which is not allowed. I am using Vue.js for frontend and webpack and vue-loader for build process
Package.json file
{
"webpack": "^3.10.0",
"babel-core": "^6.18.2",
"babel-loader": "^7.1.2",
"babel-preset-es2015": "^6.18.0",
"babel-preset-stage-2": "^6.24.1",
"file-loader": "^0.9.0",
"style-loader": "^0.18.2",
"vue-loader": "^10.0.2"
}
This is what contained within build.js file from webpack. Both Function constructor and eval usages.
try {
// This works if eval is allowed (see CSP)
g = g || Function("return this")() || (1,eval)("this");
} catch(e) {
// This works if the window reference is available
if(typeof window === "object")
g = window;
}
// Another method of build
function setImmediate(callback) {
// Callback can either be a function or a string
if (typeof callback !== "function") {
callback = new Function("" + callback);
}
This is the result of the web-ext lint which checks for issues in addon
Code Message File Line Column
DANGEROUS_EVAL The Function build.js 433 11
constructor is
eval.
DANGEROUS_EVAL eval can be build.js 433 43
harmful.
DANGEROUS_EVAL The Function build.js 8814 20
constructor is
eval.
Is there any way I can just build without Webpack using build because from side of Vue there is a support to use runtime code of Vue but Webpack has no flat to build as per CSP policy. Please help as I don't need especially this line in build

The reason is that, Webpack checks for global variables and it needed, node:false in Webpack config file which actually removes the above mentioned code. The reason, the above code is not an issue on web application because it won't run the code but in case of Chrome Extension or Firefox plugin, the code is scanned irrespective of execution and this was creating problem.
This is present in webpack source code here. More info about globals is present here.
Tried it with webpack: ^3.11.0 version, worked like charm.

This happens because of Vue.js, not Webpack.
According to the vue docs:
Some environments, such as Google Chrome Apps, enforce Content Security Policy (CSP), which prohibits the use of new Function() for evaluating expressions. The full build depends on this feature to compile templates, so is unusable in these environments.
On the other hand, the runtime-only build is fully CSP-compliant. When using the runtime-only build with Webpack + vue-loader or Browserify + vueify, your templates will be precompiled into render functions which work perfectly in CSP environments.
Unfortunately, Vue doesn't have something like ng-csp in Angular. So the only way to run your extension – to use runtime build.
There are good answers how to such build:
Vue.js in Chrome extension
Vuejs browser extension without using 'unsafe-eval' in CSP

Related

Getting undefined for my environment variables

I have angular 12 + node 14 project. Can someone please help me? I have gone through all the posts related to this. I get undefined everytime I try to use environment variable.
I've placed .env file in the folder where I have my login.component.ts as in my root folder I don't have any ts file from where I could add require('dotenv').config().
In my login.component.ts I added:
require('dotenv').config({path: 'C:/Users/.../git/../src/app/authentication/login/.env'});
My .env file as content like this CLIENT_ID=sample.
I have below in my package.json
"dotenv": "^10.0.0",
"os-browserify": "^0.3.0",
"path-browserify": "^1.0.1",
"process": "^0.11.10",
"webpack": "^5.60.0",
"dotenv-webpack": "^7.0.3"
I added all for which I was getting errors. I am trying to build and run using ng build and ng serve.
Whenever I try this in my login.component.ts I get: undefined on:
console.log('value is ', process.env.CLIENT_ID);
Angular is a frontend framework, meaning that things like 'require' or accessing the filesystem just doesn't exists in its scope. Also process isn't a thing in Angular. Just imagine if a website or a webapp could access your filesystem without you to even know it. if you want to declare frontend variables in a .env like way just create a JSON file and write something like:
{
"VAR1": "VALUE",
"VAR2": "VALUE"
}
and then import the json in your code(dynamic imports are a great way of doing that).
If you're building a web-app tho i would recommend you to build a node server that has access to the fs and make requests from angular(through the built-in httpclient) to your server and send back data from there

Mocha - Running test ReferenceError: regeneratorRuntime is not defined

I am trying to run tests with async/await using mocha. The project architecture was setup before I started working on it and I have been trying to update it's node version to 8.9.4. The project is an isomorphic application and uses babel, gulp and webpack to run.
To run the tests we run a gulp task. There are two .bablerc files in the project. One in the root folder of the project and another in the test fodler.
Both have the same configuration:
{
"presets": [
["env", {"exclude": ["transform-regenerator"]}],
"react",
"stage-1"
],
"plugins": [
"babel-plugin-root-import"
]
}
When I run the app locally there is no error returned anymore. However when I run the tests with gulp test:api I constantly get the error: ReferenceError: regeneratorRuntime is not defined
This is my gulp file in the test folder:
var gulp = require('gulp')
var gutil = require('gulp-util')
var gulpLoadPlugins = require('gulp-load-plugins')
var plugins = gulpLoadPlugins()
var babel = require('gulp-babel')
require('babel-register')({
presets:["es2015", "react", "stage-1"]
});
// This is a cheap way of getting 'test:browser' to run fully before 'test:api' kicks in.
gulp.task('test', ['test:browser'], function(){
return gulp.start('test:api')
});
gulp.task('test:api', function () {
global.env = 'test'
gulp.src(['test/unit-tests/server/**/*.spec.js'], {read: false})
.pipe(plugins.mocha({reporter: 'spec'}))
.once('error', function (error) {
console.log(error)
process.exit(1);
})
.once('end', function () {
process.exit(0);
})
});
gulp.task('default', ['test']);
Any help on why this is happening wouldd be much appreciated.
Node version 8 already has support for async/await so you do not need Babel to transform it; indeed, your root .babelrc includes this preset to exclude the regenerator that would transform async/await (and introduce a dependency on regeneratorRuntime):
["env", {"exclude": ["transform-regenerator"]}]
However, in your test file, the configuration does not specify this preset. Instead, it specifies the preset "es2015", which does include the unwanted transform-regenerator (as you can see at https://babeljs.io/docs/plugins/preset-es2015/). If you change this to match the presets in the root .babelrc, you'll get more consistent results.
Strangely i ran into this issue after i upgraded to Node v8.10.0 from v8.6.x . I had used babel-require like so in my test-setup.js
require('babel-register')();
and the testing tools are Mocha,chai,enzyme + JSDOM . I was getting the same issue when i was making a async call to a API, also while using generator functions via sagas. Adding babel-polyfill seemed to have solved the issue.
require('babel-register')();
require('babel-polyfill');
i guess even babel docs themselves advocate using polyfill for generators and such
Polyfill not included
You must include a polyfill separately when using features that require it, like generators.
Ran into the same issue when running mocha tests from within Visual Studio Code.
The solution was to add the necessary babel plugins in the Visual Studio Code settings.json :
"mocha.requires": [
"babel-register",
"babel-polyfill"
],
I've run into this error before myself when using async/await, mocha, nyc, and when attempting to run coverage. There's never an issue when leveraging mocha for running tests, just with mocha tests while leveraging nyc for running coverage.
11) Filesystem:removeDirectory
Filesystem.removeDirectory()
Should delete the directory "./tmp".:
ReferenceError: regeneratorRuntime is not defined
at Context.<anonymous> (build/tests/filesystem.js:153:67)
at processImmediate (internal/timers.js:461:21)
You can fix the issue a couple of different ways.
Method 1 - NPM's package.json:
...
"nyc": {
"require": [
"#babel/register",
"#babel/polyfill"
],
...
},
...
It really depends which polyfill package you're using. It's recommended to use the scoped (#babel) variant: #babel/pollyfill. However, if you're using babel-polyfill then ensure that's what you reference.
Method 2 - Direct Import
your-test-file.js (es6/7):
...
import '#babel/polyfill';
...
OR
your-test-file.js (CommonJS):
...
require("#babel/polyfill");
...
Don't assign it to a variable, just import or require the package. Again, using the package name for the variant you've sourced. It includes the polyfill and resolves the error.
HTH

Warnings when building backend Express/WS Node app with Webpack

I am getting some confusing warnings when building a backend Node server with Webpack. I want to use Webpack to build my backend primarily for two reasons:
Webpack creates a single executable file, which is easier to deploy
Webpack includes all of my app's dependencies, so I can deploy my app to any compatible Node environment without needing to install dependencies first
Here are the warnings I'm getting:
WARNING in ./~/ws/lib/BufferUtil.js
Module not found: Error: Can't resolve 'bufferutil' in .../node_modules/ws/lib
# ./~/ws/lib/BufferUtil.js 35:21-42
# ./~/ws/lib/Receiver.js
# ./~/ws/index.js
# ./src/main.js
WARNING in ./~/ws/lib/Validation.js
Module not found: Error: Can't resolve 'utf-8-validate' in .../node_modules/ws/lib
# ./~/ws/lib/Validation.js 10:22-47
# ./~/ws/lib/Receiver.js
# ./~/ws/index.js
# ./src/main.js
WARNING in ./~/express/lib/view.js
80:29-41 Critical dependency: the request of a dependency is an expression
For the Critical dependency warning, I've found a good example explaining the problem and some documentation on how to use the ContextReplacementPlugin, although it's still unclear to me how to apply it to this situation. It looks like the warning is being caused by line 80 in node_modules/express/lib/view.js:
opts.engines[this.ext] = require(mod).__express
It is clear to me that the dependency cannot be resolved at build time, so how can I use the ContextReplacementPlugin to fix this dependency?
As for the Module not found warnings in the ws package, it's unclear to me what's going on. It looks like those dependencies exist in my global node_modules, and maybe they're not being pulled in by Webpack. I've tried adding them to my project's devDependencies, but then I just get Critical dependency warnings for them instead.
My application still runs after being built, so I suppose I could technically ignore the warnings, but I figure that these are widely used Node packages and Webpack is a popular build tool, so there must be a reasonable solution available.
Here are my dependencies in my package.json:
"devDependencies": {
"#types/cassandra-driver": "^0.8.10",
"#types/express": "^4.0.35",
"#types/uuid": "^2.0.29",
"#types/ws": "0.0.40",
"nodemon": "^1.11.0",
"typescript": "^2.3.1",
"webpack": "^2.5.1"
},
"dependencies": {
"cassandra-driver": "^3.2.1",
"express": "^4.15.2",
"uuid": "^3.0.1",
"ws": "^2.3.1"
}
And here's my webpack.config.js:
const path = require('path');
module.exports = {
entry: './src/main.js',
output: {
path: path.join(__dirname, 'dist'),
filename: 'main.js'
},
target: 'node',
node: {
__dirname: false,
__filename: false
}
};
I like keeping things minimal if possible. Thanks for reading.
The short answer
webpack can work with node, but it cannot extract follow require() statements. Modifications have to be made to ignore require() in order for it to work.
The long answer
It is actually possible to pull some files into a master file and run it in some instances.
One instance is if all the modules required are written in typescript and the modules are written in a away that the typescript plugin can parse the module in.
Another instance would be if you are using es6 babel plugins and using es6 style imports.
Even in the above scenarios the blunder may choose not to pull in certain files
The ultimate answer
It really should not matter too much about trying to perform the long answer because modules are stored in memory at boot and then referenced later from the cache. See the article below for more information.
http://fredkschott.com/post/2014/06/require-and-the-module-system/

Proper use of karma-commonjs with Jasmine 2

I've spent a fair amount of time trying to debug this, and figured I would ask. I even created a GitHub repository but won't rely on it, so here goes. I'm trying to take advantage of CommonJS syntax within the Karma test runner using PhantomJS. For my module I created the simplest thing I could think of:
exports.returnYes = function() {
return "Yes";
};
The Jasmine test is:
var returnYes = require("../js/returnYes").returnYes;
describe("returnYes", function() {
it("should return Yes", function() {
expect(returnYes()).toBe("Yes");
});
});
And, if I do a jasmine init I can run it from the command line thanks to jasmine-npm by simply typing jasmine with output:
$ jasmine
Started
.
1 spec, 0 failures
Finished in 0.003 seconds
Now to try and get it to work inside karma:
I create my karma.conf.js with frameworks: jasmine,commonjs. And, I add commonjs as preprocessor.
I try to do a karma run and I find that it can't find global which is part of getJasmineRequireObj in jasmine.js where it declares jasmineGlobal = global;
The command line output is a little hard to read, but here it is:
$ karma run
[2015-06-27 17:41:35.266] [DEBUG] config - Loading config /Users/zen/Projects/karma-commonjs-test/karma.conf.js
##teamcity[enteredTheMatrix]
##teamcity[testSuiteStarted nodeId='1' parentNodeId='0' name='karma.conf.js' nodeType='config' locationHint='config:///Users/zen/Projects/karma-commonjs-test/karma.conf.js']
##teamcity[testSuiteStarted nodeId='2' parentNodeId='1' name='PhantomJS 1.9.8 (Mac OS X 0.0.0)' nodeType='browser']
##teamcity[testStarted nodeId='3' parentNodeId='2' name='Error' nodeType='browserError']
##teamcity[testFailed nodeId='3' error='yes' message='ReferenceError: Can|'t find variable: global|nat http://localhost:9876/base/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?68f13ab3f93af5a219b9fe8409f8763b31998bba:27']
##teamcity[testSuiteFinished nodeId='2']
##teamcity[testSuiteFinished nodeId='1']
For good measure here are the devDependencies in my packages.json:
"devDependencies": {
"jasmine-core": "^2.3.4",
"karma": "^0.12.37",
"karma-commonjs": "0.0.13",
"karma-jasmine": "^0.3.5",
"karma-phantomjs-launcher": "^0.2.0",
"phantomjs": "^1.9.17"
}
I'm not sure why I can't find global. Any help would be greatly appreciated!!! :)
It seems like my whole problem came down to the line in karma.conf.js (not shown in my original question:
preprocessors: {
'**/*.js': ['commonjs']
},
For some reason, jasmine.js is not happy being pre-processed by commonjs, and "**/*.js" says to go through all subdirectories (which is probably overkill), including node_modules which has jasmine-core/jasmine.js
So I can either make my pre-processor more specific (best practice):
preprocessors: {
'spec/*.js': ['commonjs'],
'js/*.js': ['commonjs']
},
but as a test to see if any other files would give me a problem, I tried:
preprocessors: {
'**/!(jasmine).js': ['commonjs'],
},
And, everything worked as well. Bottom line. Do not process jasmine.js through commonjs preprocessor!

how to require a specific file using duojs

I need to include a library that is present on github, but is not well-packaged; using Duo.js
At the moment of writing I am using the following to achieve what I desire:
bower
gulp
main-bower-files
Bower just downloades the library.
Gulp, with main-bower-files are useful to override the single package options and setup a so-called "main file" that I can build.
Example:
gulp.task('copy-libs', function () {
return gulp.src(bowerFiles({ env: 'development' }))
.pipe(gulp.dest('build/libs/'));
});
bower.json file:
"dependencies": {
"cash": "sudo-js/cash",
"bootstrap": "~3.3.2",
"delorean": "~0.8.7",
"react": "~0.12.2"
},
"overrides": {
"cash": {
"main": {
"development": "build/debug/cash.js"
}
}
}
}
How can i achieve this with duojs?
The documentation is quite thin regarding libraries that does not ship with a valid component.json
You can specify the path to an entry file for your lib. It won't be as clean as just specifying user/repo, but it'll get the job done.
For example, when including Twitter Bootstrap from twbs/bootstrap
require('twbs/bootstrap#v3.3.2:dist/js/bootstrap.js');
// repo: twbs/bootstrap
// version/tag: v3.3.2
// path: dist/js/bootstrap.js
Unfortunately, this doesn't work out-of-the-box since it assumes you have the jQuery global... so you need to add this above the previous line.
jQuery = require('components/jquery'); // leave out `var` so it becomes a global
This includes jQuery from the wonderful components project. (they package up popular libs so they can be consumed by various package managers.
Also, it turns out there is a components/bootstrap that is properly packaged with a component.json.
So, you can actually make bootstrap work with the following:
jQuery = require('components/jquery');
require('components/bootstrap');
For the other libraries that aren't as common, you can use the process mentioned first to specify the path to the right JS/CSS file. (ie: user/repo#version:path)

Resources