A webpack plugin which runs JSDefender on every chunk using the compiler's "emit" hook.
Supported webpack versions: ^3.0.0, ^4.0.0, ^5.0.0
Installation
Install the plugin to your project:
npm install <package-directory>/preemptive-jsdefender-core-{version}.tgz <package-directory>/preemptive-jsdefender-webpack-plugin-{version}.tgz --save-dev
OR
yarn add file:<package-directory>/preemptive-jsdefender-core-{version}.tgz file:<package-directory>/preemptive-jsdefender-webpack-plugin-{version}.tgz --dev
@preemptive/jsdefender-core as its peerDependency; this is why you must also install it.Usage
Production Mode and Minifiers
By default, when you run webpack in production mode, it applies optimizations that remove JSDefender inline protection directives from the code. As a result, JSDefender will not be able to use your inline protection settings. To prevent webpack from stripping the directives, you need to modify its optimization settings. Though you can turn off optimization entirely, we do not encourage that. Instead, change the default minification to one that preserves directives.
The following webpack.config.js code snippet utilizes the terser-webpack-plugin:
const TerserPlugin = require("terser-webpack-plugin");
//...
module.exports = {
// Use the other settings as you would do
// ...
// Modify optimization settings
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({ // Use TerserPlugin as a minifier
terserOptions: {
compress: { // Preserve directives
directives: false,
},
},
}),
],
},
// ...
// Other settings
}
Plain Webpack
Import the jsdefender-webpack-plugin in your webpack config (by default it is called webpack.config.js) and add it to the plugins array:
const { JSDefenderWebpackPlugin } = require('@preemptive/jsdefender-webpack-plugin');
// --Other configuration
plugins: [
// --Other plugins
new JSDefenderWebpackPlugin({
configurationFile: 'jsdefender.config.json', // Optional path to the configuration file, if any, it defaults to `jsdefender.config.json`
quietMode: false, // If false every log message will be displayed, otherwise only the errors and warnings, it defaults to false
enableInDevelopmentMode: false, // If false the protection will be skipped in non-production modes, otherwise it will run in every mode, it defaults to false
includeChunks: [ 'app', 'util' ], // The chunk names that should be protected, by default every chunk is included
excludeChunks: [ 'vendor', 'util' ], // The chunk names that should not be protected, be default nothing is excluded
/* Other JSDefender options e.g. `settings: { booleanLiterals: true, stringLiterals: true }` etc. could also be provided here */
})
]
enableInDevelopmentMode option of the plugin to true.booleanLiterals: true provided in the configuration file and booleanLiterals: false provided directly in the plugin's constructor, then the final value is going to be booleanLiterals: false.includeChunks and excludeChunks then the exclusion takes precedence.Angular
Angular hides the webpack config from the developers by default and it can be modified in different ways for the different versions of Angular.
The Webpack configuration of the JSDefender Webpack Plugin is the same for every Angular version:
const { JSDefenderWebpackPlugin } = require('@preemptive/jsdefender-webpack-plugin');
...
module.exports = {
...
plugins: [
new JSDefenderWebpackPlugin({
/* All the configuration from the above example can be used here, plus: */
excludeChunks: [ 'vendor', 'scripts', 'common', 'runtime', 'polyfills', 'polyfills-es5', 'styles', 'inline' ] // vendor chunk should be always excluded, but other system chunks could also have problems, especially the polyfills
})
]
...
}
Be sure to not include the vendor chunk because it has some deprecated code in it that JSDefender cannot protect. If you still encounter problems after excluding the vendor chunk, then you should try to exclude other system chunks starting with the polyfills and polyfills-es5, but if you want maximal performance, exclude every system chunk specified in the example config above. The exclusion can be achieved either by including the app's chunks one-by-one, or by excluding the appropriate chunks. Of course you can also include or exclude any other chunk.
The provided configurations should work either with Angular or Angular Universal (Server Side Rendering).
Tested Angular versions: ^5.0.0, ^8.0.0, ^9.0.0, ^11.0.0, ^14.0.0
Angular 2-5
In order to be able to edit the webpack.config.js in an Angular 2-5 project, it must be ejected first because it is hidden by default. To eject, run the following command:
ng eject
After ejecting, add the JSDefenderWebpackPlugin to the plugins array in the webpack.config.js as shown in the Angular section.
Then build or serve the project by using the --vendor-chunk=true flag in --prod mode to generate a separate vendor bundle so you can exclude it from protection. In a development build, the vendor chunk is automatically generated.
ng build --prod --vendor-chunk=true
ng serve --prod --vendor-chunk=true
Now you should see the protected chunks.
Angular 6+
Since Angular 6+, the webpack.config.js is not editable and it cannot be ejected, but custom Angular builders can be used to modify the webpack config.
Create a file called webpack.partial.js in your Angular project and add the configuration there as shown in the Angular section.
Enable the creation of the separate vendor chunk in production builds by either setting the "vendorChunk": true in the angular.json's production build configuration or by adding the --vendorChunk=true flag to the build script. Learn more at the ng build documentation.
Using the custom-webpack builder
The @angular-builders/custom-webpack builder makes it possible to provide a path to a custom webpack configuration in the angular.json file. There are other builders you can use to extend Angular's Webpack configuration, but we recommend custom-webpack. Make sure to use the version compatible with your Angular version!
Install the package:
npm install --save-dev @angular-builders/custom-webpack@{version}
OR
yarn add --dev @angular-builders/custom-webpack@{version}
Modify the project's angular.json configuration:
"architect": {
...
"build": {
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "./webpack.partial.js"
},
...
}
},
"serve": {
"builder": "@angular-builders/custom-webpack:dev-server",
"options": {
"browserTarget": "{project-name}:build" // Change {project-name} to your project's name
}
}
Then build or serve your Angular project and see the protected chunks.
Using the ngx-build-plus builder
The ngx-build-plus builder makes it possible to provide a custom webpack configuration through the Angular CLI (ng) with the --extra-webpack-config flag. Make sure to use the version compatible with your Angular version!
Install the package via the Angular CLI:
ng add ngx-build-plus@^{your-angular-major-version}.0.0
The next step is to add the partial webpack configuration to your build, or run scripts with the --extra-webpack-config flag in your package.json:
"scripts": {
"build:prod": "ng build --extra-webpack-config webpack.partial.js --prod",
"serve:prod": "ng serve --extra-webpack-config webpack.partial.js --prod",
}
Then build or serve your Angular project and see the protected chunks.
Ionic
Ionic hides the webpack config from the developers by default, but it can be modified in different ways for the different versions of the framework.
Tested Ionic + front-end framework version pairs:
- Ionic
^3.0.0+ Angular^5.0.0 - Ionic
^5.0.0+ Angular^8.0.0 - Ionic
^5.0.0+ React^16.0.0
Ionic 2-3
In Ionic 2-3 you can provide your own webpack config which can extend Ionic's own configuration.
First, add "ionic_webpack" configuration to the "config" section in the project's package.json.
{
...
"config": {
"ionic_webpack": "./webpack.partial.js"
}
...
}
Then, add a file called webpack.partial.js to the project root and add the following configuration. Save the file, then configure the JSDefenderWebpackPlugin as in the Angular section above.
const { dev, prod } = require('@ionic/app-scripts/config/webpack.config');
const webpackMerge = require('webpack-merge');
const { JSDefenderWebpackPlugin } = require('@preemptive/jsdefender-webpack-plugin');
const webpackJSDefenderConfig = {
plugins: [
new JSDefenderWebpackPlugin({
/* Same configuration as in the Angular section above, make sure to exclude the vendor chunk */
})
]
};
module.exports = {
dev: webpackMerge(dev, webpackJSDefenderConfig),
prod: webpackMerge(prod, webpackJSDefenderConfig)
};
The vendor chunk is always generated for Ionic 2-3 projects, so no additional configuration is needed other than excluding it in the JSDefenderWebpackPlugin config.
Ionic 4+
Since Ionic 4, there is no custom webpack config created by Ionic. Instead, the inner front-end framework's (Angular, React, Vue) own solution is used.
Angular 6+
The configuration from the above Angular 6+ section can be used for Ionic 4+ and Angular 6+ projects. So a custom builder needs to be added to extend the default webpack config.
React 16+
To circumvent the predicament of no custom webpack config available, there are two possible ways to make an Ionic-React project work with the webpack plugin (use either one, not both of them!):
1. Eject
Eject from the managed React environment with npm run eject or npx react-scripts eject.
Proceed with the plugin installation outlined above under Usage -> Plain Webpack.
2. Use react-app-rewired
There is a third-party package that helps in accessing the Webpack configuration options (link). This is an added dependency, nevertheless a necessary one.
- Install the package to your project:
npm install react-app-rewired --save-dev - Add the following to your package.json scripts (modify existing script keys if necessary):
"start": "react-app-rewired start",
"build": "react-app-rewired build",
- Add a
config-overrides.jsto the root of the project with the following lines:
const { JSDefenderWebpackPlugin } = require('@preemptive/jsdefender-webpack-plugin');
module.exports = function override(config, env) {
if (!config.plugins) {
config.plugins = [];
}
config.plugins.push(
new JSDefenderWebpackPlugin({
configurationFile: './jsdefender.config.json',
excludeChunks: [ 'vendor', 'scripts', 'common', 'runtime', 'polyfills', 'polyfills-es5', 'styles' ]
})
);
return config;
}
- Set up a JSDefender configuration according to the configuration guide below.
- Finally, run
npm run buildoryarn buildto have the webpack plugin protect your code.
Configuration
The JSDefender webpack plugin accepts the same configuration object as the JSDefender configuration file. You can learn more about it at the documentation home. The plugin uses a default configuration just as JSDefender CLI does. This can be overridden by explicitly specifying a configuration to the plugin.
configurationFile is present, its individual configuration lines will be overwritten by the JSDefender configuration directly passed to the new JSDefenderWebpackPlugin() constructor in the webpack.config.js or webpack.partial.js Webpack config file. For example, if you set booleanLiterals: true in the JSDefenderWebpackPlugin constructor but there is a configurationFile set which has booleanLiterals: false, then the first will take precedence, so the final value will be booleanLiterals: true.