Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

fix: don't inline css in csp mode. #4411

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ module.exports = function(grunt) {
src: util.wrap([files['angularSrc']], 'angular'),
styles: {
css: ['css/angular.css'],
generateCspCssFile: true,
minify: true
}
},
Expand Down
41 changes: 32 additions & 9 deletions lib/grunt/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ var shell = require('shelljs');
var grunt = require('grunt');
var spawn = require('child_process').spawn;
var version;
var CSP_CSS_HEADER = '/* Include this file in your html if you are using csp mode. */\n\n';

module.exports = {

Expand Down Expand Up @@ -70,12 +71,20 @@ module.exports = {


addStyle: function(src, styles, minify){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would change the addStyle function to this:

addStyle: function(src, styles, minify) {
    return styles.reduce(function(result, file) {
      var css = fs.readFileSync(file).toString();

      result.concatenatedCss += css + '\n';
      result.js += processCSS(css, minify);

      return result;
    }, {js: src, concatenatedCss: ''});

    function processCSS(css, minify) {
      // minify
      css = css
          .replace(/\r?\n/g, '')
          .replace(/\/\*.*?\*\//g, '')
          .replace(/:\s+/g, ':')
          .replace(/\s*\{\s*/g, '{')
          .replace(/\s*\}\s*/g, '}')
          .replace(/\s*\,\s*/g, ',')
          .replace(/\s*\;\s*/g, ';');

      // escape for JS
      css = css
        .replace(/\\/g, '\\\\')
        .replace(/'/g, "\\'")
        .replace(/\r?\n/g, '\\n');

      return "if (!document.securityPolicy || !document.securityPolicy.isActive) { angular.element(document).find('head').prepend('<style type=\"text/css\">" + css + "</style>'); }";
    }
  },

styles = styles.map(processCSS.bind(this)).join('\n');
src += styles;
return src;
styles = styles.reduce(processCSS.bind(this), {
js: [src],
css: []
});
return {
js: styles.js.join('\n'),
css: styles.css.join('\n')
};

function processCSS(state, file) {
var css = fs.readFileSync(file).toString(),
js;
state.css.push(css);

function processCSS(file){
var css = fs.readFileSync(file).toString();
if(minify){
css = css
.replace(/\r?\n/g, '')
Expand All @@ -87,11 +96,14 @@ module.exports = {
.replace(/\s*\;\s*/g, ';');
}
//escape for js
css = css
js = css
.replace(/\\/g, '\\\\')
.replace(/'/g, "\\'")
.replace(/\r?\n/g, '\\n');
return "angular.element(document).find('head').prepend('<style type=\"text/css\">" + css + "</style>');";
js = "if (!document.securityPolicy || !document.securityPolicy.isActive) { angular.element(document).find('head').prepend('<style type=\"text/css\">" + js + "</style>'); }";
state.js.push(js);

return state;
}
},

Expand All @@ -112,17 +124,28 @@ module.exports = {
build: function(config, fn){
var files = grunt.file.expand(config.src);
var styles = config.styles;
var processedStyles;
//concat
var src = files.map(function(filepath){
var src = files.map(function(filepath) {
return grunt.file.read(filepath);
}).join(grunt.util.normalizelf('\n'));
//process
var processed = this.process(src, grunt.config('NG_VERSION'), config.strict);
if (styles) processed = this.addStyle(processed, styles.css, styles.minify);
if (styles) {
processedStyles = this.addStyle(processed, styles.css, styles.minify);
processed = processedStyles.js;
if (config.styles.generateCspCssFile) {
grunt.file.write(removeSuffix(config.dest) + '-csp.css', CSP_CSS_HEADER + processedStyles.css);
}
}
//write
grunt.file.write(config.dest, processed);
grunt.log.ok('File ' + config.dest + ' created.');
fn();

function removeSuffix(fileName) {
return fileName.replace(/\.js$/, '');
}
},


Expand Down
3 changes: 2 additions & 1 deletion src/ng/directive/ngCloak.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
* of the browser view.
*
* `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
* `angular.min.js`:
* `angular.min.js`.
* For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
*
* <pre>
* [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
Expand Down
4 changes: 4 additions & 0 deletions src/ng/directive/ngCsp.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
* directive will cause Angular to use CSP compatibility mode. When this mode is on AngularJS will
* evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will
* be raised.
*
* CSP forbids JavaScript to inline stylesheet rules. In non CSP mode Angular automatically
* includes some CSS rules (e.g. {@link ng.directive:ngCloak ngCloak}).
* To make those directives work in CSP mode, include the `angular-csp.css` manually.
*
* In order to use this feature put the `ngCsp` directive on the root element of the application.
*
Expand Down
2 changes: 2 additions & 0 deletions src/ng/directive/ngShowHide.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* provided to the ngShow attribute. The element is shown or hidden by removing or adding
* the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
* in AngularJS and sets the display style to none (using an !important flag).
* For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
*
* <pre>
* <!-- when $scope.myValue is truthy (element is visible) -->
Expand Down Expand Up @@ -161,6 +162,7 @@ var ngShowDirective = ['$animate', function($animate) {
* provided to the ngHide attribute. The element is shown or hidden by removing or adding
* the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
* in AngularJS and sets the display style to none (using an !important flag).
* For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
*
* <pre>
* <!-- when $scope.myValue is truthy (element is hidden) -->
Expand Down