Tips for setting up a Vue application (using vue-cli) to include nonce attributes in created script tags

My vue application, compiled using vue-cli, has a vue.config.js file structured as follows:

'use strict';

module.exports = {
  publicPath: `${process.env.CDN_URL || ''}/dist/`,
  lintOnSave: true,
  transpileDependencies: [],
  outputDir: '.tmp/dist',
  pages: {
    navigator: {
      entry: 'vue/home/main.ts',
      template: 'views/home/index.ejs',
      // Will output to dist/views/home/index.ejs
      filename: 'views/home/index.ejs',
    },
  },
  chainWebpack(config) {
    config.module
      .rule('ejs')
      .test(/\.ejs$/)
      .use('html')
      .loader('html-loader');
  },
};

I want webpack to include

nonce="<%= nonce %>"
for each generated script tag. I have tried setting the __webpack_nonce__ variable in various sections of vue.config.js without success. Attempts include adding it to chainWebpack() and configWebpack(), as well as placing it in vue/home/main.ts file. None of these approaches have yielded the desired result. How can I successfully add nonce attributes to the script tags?

The specific versions being used are vue 2.6.x and vue cli 4.5.x

Answer №1

Due to the complexity of my requirements, I decided to create a custom webpack plugin instead of relying on the script-ext-html-webpack-plugin. Specifically, I needed to use ${nonce} for header tags and <%= nonce %> for body tags. My plugin is simple and inspired by the script-ext-html-webpack-plugin referenced by @Sphinx.

const HtmlWebpackPlugin = require('html-webpack-plugin');

class AddNonceToScriptTagsWebpackPlugin {
  apply(compiler) {
    compiler.hooks.compilation.tap(this.constructor.name, (compilation) => {
      const alterAssetTagGroups = compilation.hooks.htmlWebpackPluginAlterAssetTags || HtmlWebpackPlugin.getHooks(compilation).alterAssetTagGroups;
      alterAssetTagGroups.tap(this.constructor.name, (data) => {
        data.head = this._addNonceAttribute(data.head || []);
        data.body = this._addNonceAttribute(data.body || []);

        return data;
      });
    });
  }

  _addNonceAttribute(tags) {
    return tags.map((tag) => {
      if (tag.tagName === 'script') {
        tag.attributes = tag.attributes || {};
        tag.attributes.nonce = '<%= nonce %>';
      } else if (tag.tagName === 'link' && tag.attributes && tag.attributes.as === 'script') {
        tag.attributes = tag.attributes || {};
        // eslint-disable-next-line no-template-curly-in-string
        tag.attributes.nonce = '${nonce}';
      }

      return tag;
    });
  }
}

The vue.config.js file has been updated as follows:

'use strict';

module.exports = {
  publicPath: `${process.env.CDN_URL || ''}/dist/`,
  lintOnSave: true,
  transpileDependencies: [],
  outputDir: '.tmp/dist',
  pages: {
    navigator: {
      entry: 'vue/home/main.ts',
      template: 'views/home/index.ejs',
      // Will output to dist/views/home/index.ejs
      filename: 'views/home/index.ejs',
    },
  },
  configureWebpack: {
    plugins: [
      new AddNonceToScriptTagsWebpackPlugin(),
    ],
  },
  chainWebpack(config) {
    // Override the default loader for html-webpack-plugin so that it does not fallback to ejs-loader.
    // ejs-loader will use ejs syntax against the template file to inject dynamic values before html-webpack-plugin runs
    config.module
      .rule('ejs')
      .test(/\.ejs$/)
      .use('html')
      .loader('html-loader');
  },
};

Answer №2

To solve this issue, consider incorporating the script-ext-html-webpack-plugin into the list of plugins in your webpack.prod.conf file (or whichever webpack configuration file you are using).

new ScriptExtHtmlWebpackPlugin({
  custom: {
    test: /\.js$/, // modify this regular expression as needed
    attribute: 'nonce',
    value: '<%= nonce %>'
  }
}),

Answer №3

After 3 years of searching, I am still trying to find the perfect solution here

I have made some modifications and clarifications based on @Jim Geurts' solution

To your vue.config.js file, add the following:

class AddNonceToScriptTagsWebpackPlugin {
  apply(compiler) {
    compiler.hooks.compilation.tap(this.constructor.name, (compilation) => {
      const alterAssetTagGroups = compilation.hooks.htmlWebpackPluginAlterAssetTags || HtmlWebpackPlugin.getHooks(compilation).alterAssetTagGroups;
      alterAssetTagGroups.tap(this.constructor.name, (data) => {
        data.head = this._addNonceAttribute(data.head || []);
        data.body = this._addNonceAttribute(data.body || []);

        return data;
      });
    });
  }

  _addNonceAttribute(tags) {
    return tags.map((tag) => {
      if (tag.tagName === 'script' || tag.tagName === 'link' || tag.tagName === 'style') {
        tag.attributes = tag.attributes || {};
        tag.attributes.nonce = 'YOUR-SECRET-NONCE';
      }

      return tag;
    });
  }
}

However, be aware that utilizing Jim's code or mine will result in the HTML output (index.html) being minified inline, removing spaces and line breaks. The solution lies in customizing the HtmlWebpackPlugin configuration.

  configureWebpack: {
    plugins: [
      new HtmlWebpackPlugin({
        template: 'public/index.html',
        inject: 'body', // Inject scripts into body
        minify: false, // Disable minification
      }),
      new AddNonceToScriptTagsWebpackPlugin(),
    ]
  }

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

I'm currently attempting to incorporate the Material-UI InfoIcon into my code, but I'm unsure of how to properly integrate it within a TextField component

I am attempting to integrate the Material-UI InfoIcon into my TextField code, but I'm unsure of how to go about it. Here is the snippet of Material-UI code: <InfoIcon fontSize="small" /> This is where I would like to place it: <Grid item ...

Deleting a document in Mongo with the use of an object as a value

Hello, I'm struggling with deleting a document from my MongoDb using an object. Let me explain: const removeDocument = function (db, callback) { // Access the collection of documents const collection = db.collection(documentName) // Delete the ...

Perform ng-repeat on an array containing several other arrays

This is an angularjs function that retrieves specific categories. $scope.getSpecificCat = function(p_cat) { $http.get(url+'getSpecificCatJson/' + p_cat).success(function(data){ $scope.specifics = data; }).error(functi ...

How to use jQuery to delete the final table in an entire HTML document

This situation is really frustrating: When I make a jQuery Ajax call, the success callback returns an entire HTML page. The returned page looks like this: <html> <head> <title>Title</title> <body> <table></table> ...

Utilizing Vue.js to ensure that nested components remain within the parent component even when the store

I have been working on a chat messaging system, where I initially populate the store with root messages and then map the state of that list (array). Everything works fine when posting a new message - the store updates and displays the new post. However, I ...

Create JavaScript code with PHP

Looking to dynamically generate the column definitions for a Datatable. Here are the columns: "columns": [ { "data": "id", "orderable": false }, { "data": "code" }, { "data": "name" }, { "data": "created", " ...

In order to effectively utilize React and Webpack, it is vital to have the right loader at your disposal

While attempting to incorporate React.js with Webpack, I encountered an issue when running "npm run build." The error message displayed is as follows: You may need an appropriate loader to handle this file type, as currently no loaders are set up to proce ...

Using vuex-class to interact with Vuex in non-Vue components

Is it possible to access Vuex outside of a Vue component using vuex-class? In a typical scenario, the process is quite straightforward: // some JS file import store from './../store'; // path to Vuex store store.commit('ux/mutationName&ap ...

Confirm the drag-and-drop functionality for customers by utilizing the accept feature

Is it possible to customize my draggable & droppable module with these classes? stackDrop1 (Kitchen) stackDrop2 (Road) stackDrop3 (Completed) https://i.sstatic.net/vxFCx.jpg If the Customer George is in the Road class, the Kitchen class will not accept ...

I created a custom discord.js-commando command to announce all the channels that my bot is currently active in, however, encountered an unexpected error

const Commando = require('discord.js-commando'); module.exports = class AnnounceCommand extends Commando.Command { constructor(client) { super(client, { name: 'announce', aliases: ['an'], ...

Guide to utilizing Vuex store to update values from a child component (settings page) in a Vue.js application

I am currently working on implementing a "settings" component that will store selected values in a centralized location for other components to access and update their appearance accordingly. SettingsView.vue: One of the settings (you can also view it on ...

Is it possible to loop through a subset of a collection using *ngFor?

Is it possible to iterate through a specific range of elements in a collection using *ngFor? For instance, I have a group of checkboxes with their form control name and label specified as follows: [{id: 'c1', label: 'C1'}, ...] Assum ...

Execute a node script file at random intervals

I've been utilizing forever to maintain the continuous execution of a node script: forever start script1.js However, my requirement is to run these files in a random order... For instance: Execute node script1.js Execute node script2.js Run script ...

Error encountered: Vue Node.js/express application is unable to set the property "render" due to

I recently set up a Vue CLI3 app and everything was running smoothly with npm run serve. However, when I tried to deploy the app by running npm run build and then using Express to launch it, I encountered an error in the console: cannot set property render ...

Creating arrays in JavaScript with or without the use of jQuery

In my JavaScript script, I am defining this array: var status=[ { "name":"name1", "level":0 }, { "name":"name2", "level":0 }, { "name":"name3", "level":0 }, { "name":" ...

Bringing joy to a JS library: Embracing both Node and the Window

I have developed a JS library that I want to convert into a Node module for use with Node.js. This library extends the Canvas context API and relies on the use of getImageData(). Therefore, it begins with a defensive check to ensure compatibility: if (wi ...

Warning: Unidentified JavaScript alert(notification) encountered without declaring a

Imagine this scenario - when I type the following command: open google.com I need JavaScript to detect "open google.com" and prompt me with an alert. The challenge is figuring out how to generate an alert for just "google.com" without including "open". ...

Using solely JavaScript, the process of eliminating parameter values from a URL on the subsequent page

Seeking assistance in removing the values from the URL after the "?" once transitioning to the next page from the initial page. Despite multiple attempts, I have been unable to find the correct solution. Any help would be greatly appreciated. Desired URL ...

Each time I scroll, the object reloads itself

I am currently facing an issue with a 3D fridge model on my website. Despite successfully loading it, I am encountering a problem where the model refreshes every time it is dragged across the screen by the user scrolling. Please refer to the linked video f ...

Encountered a module build failure due to an error code: ECONNRESET was

I am facing some errors that I cannot figure out how to solve. These errors occur when deploying my Laravel website to the server and running npm run dev. Interestingly, these errors do not occur on my local machine which has left me utterly confused =( T ...