Fomantic-UI icon indicating copy to clipboard operation
Fomantic-UI copied to clipboard

[Content Security Policy] Make the framework CSP-compliant

Open lubber-de opened this issue 6 years ago β€’ 21 comments

Feature Request

Description

Basically it seems, to make the framework CSP compliant, all the places using inline styles by $().attr('style','something') should be replaced by $().css()

Original SUI Issue

https://github.com/Semantic-Org/Semantic-UI/issues/5630

Original SUI PR

https://github.com/Semantic-Org/Semantic-UI/pull/5660

The original PR only covered transition, but .attr('style') is also used in accordion, shape, dropdown, modal

lubber-de avatar Oct 29 '18 14:10 lubber-de

I can pick this up! Is there anything else required beyond this .css change? Is there a CLA I need to sign? Thanks

dylmye avatar Mar 07 '19 14:03 dylmye

@dylmye As far as I am aware the CSS change is the only thing @lubber-de should be able to give more input on this because he has been doing a lot of security changes recently.

As for contributing all you need to do is read the following CONTRIBUTION guide and our CODE OF CONDUCT, no CLA 😎

y0hami avatar Mar 07 '19 14:03 y0hami

@hammy2899 brilliant! I've joined the Discord now so I can keep this more on topic. I'll see what I can do when I have time. πŸŽ‰

dylmye avatar Mar 07 '19 14:03 dylmye

Hello! Sorry it took so long to get on this, I've been super busy.

It appears that the only remaining places that we're using the $().attr('style', [...]) syntax is to unset the attribute before calling $().removeAttr('style') or to check that the attribute is set, eg https://github.com/fomantic/Fomantic-UI/blob/master/dist/components/accordion.js#L353-L377

Is there any reason why we still need to unset the attribute before calling removeAttr, was this an ie8 fix?

Thanks


edit 1: with the exception of one line in transition where we're using overrideStyle - I'll make sure to replace this, but this is blocked by the fact that .css ignores !important

dylmye avatar Apr 01 '19 18:04 dylmye

@dylmye Deeply test the transition module, it temporary relies on inline block display statements using "!important" to make sure it gets overridden. It also tries to determine if there might be inline styles and works with them if given. Also to make sure to correctly reset the content after the transition.

Regarding the .attr('style','')...don't really know why it was implemented (in 2014, the commit log does not reveal any explaining informations unfortunately). We have to test if simply removing it still works in current browsers. shape, accordion, transition and modal are affected

lubber-de avatar Apr 02 '19 15:04 lubber-de

I love the work you all have done on this! But I unfortunately can't use it as you don't allow theming. It's unfortunate that the Semantic UI crowd still hasn't accepted the PR for CSP compliance. πŸ˜“The transition.js included in that PR remains unchanged two years later.πŸ˜’

If changing the attr() to css() is all it takes to update the framework for CSP compliance, exactly which folder do I make the changes in? dist or src? I'm running Semantic UI from the assets folder in my Rails app, and neither seems to have had an impact on the modal.js use of inline styling.

birthdaycorp avatar Aug 17 '20 18:08 birthdaycorp

exactly which folder do I make the changes in? dist or src?

If you want to contribute to Fomantic and provide a PR, pleasse always use the src folder. The dist folder is generated on build autmatically

lubber-de avatar Aug 17 '20 19:08 lubber-de

πŸ€” Well, to be honest: After looking at the original SUI issue again, i really don't understand why the change from attr('style','...') to .css({}) makes any difference in CSP. Could somebody explain? I really doubt the original PR really makes that code csp compliant.

Both variants result in the exact same node change, both add the inline style attribute. See https://jsfiddle.net/lubber/v3ey2w9x/3/

Both methods will add style='border:1px solid black' to the nodes

image image

For my (now) understanding to be CSP compliant, absolutely no inline styles should be used, which will be difficult, especially for the transition module, which uses that technology to dynamically override the display-style (which is not necessarily block all the time

lubber-de avatar Aug 17 '20 19:08 lubber-de

Yeah that was my thought exactly. I thought I was crazy! .css() adds inline styling too.

birthdaycorp avatar Aug 17 '20 19:08 birthdaycorp

But I unfortunately can't use it as you don't allow theming.

Please explain: You can theme fomantic via LESS just like you can with SUI...What am i missing that makes you think we dont allow theming?

lubber-de avatar Aug 17 '20 19:08 lubber-de

But I unfortunately can't use it as you don't allow theming.

Please explain: You can theme fomantic via LESS just like you can with SUI...What am i missing that makes you think we dont allow theming?

I was mistaken! I was looking at an issue in the Fomantic-UI-SASS gem repo. That doesn't allow theming. This does!

birthdaycorp avatar Aug 17 '20 19:08 birthdaycorp

Yes, the SASS repo is precompiled for Ruby and not maintained by the core team of fomantic. We only offered to host that repo under the fomantic organization. Please create an issue at https://github.com/fomantic/Fomantic-UI-SASS to convince the developer to convert the LESS of fomantic into equivalent SASS to make that repo name more "worth" πŸ˜‰

lubber-de avatar Aug 17 '20 19:08 lubber-de

Hi guys, it's been a long time since I left the PR for review, however my approach does not aim to prevent the style from being added in a linear way, my PR focuses on avoiding using the .attr() function because it allows adding any attribute that is NOT IT'S STYLES, that's why CSP is complaining.

fernandops26 avatar Aug 31 '20 04:08 fernandops26

@fernandops26 Thank you for the clarification πŸ‘ , i really wasn't aware of this. Here is a codepen (jsfiddle does not allow meta tags) to reproduce the issue

https://codepen.io/lubber/pen/Yzqxxmp

Setting the styles via .attr() (first line) violates the CSP rule, while .css() (second line) still works.

However, as already mentioned in the issue description: .attr() is used in many more components, not just the transition module. So a proper fix should cover all modules.

In case codenpen does not work, here's a short source-snippet to reproduce

<html>
<head>
  <meta http-equiv="Content-Security-Policy" content="style-src 'self'">
</head>
<body>
<div id="test1">
  I am a test 1
</div>
<div id="test2">
  I am a test
</div>
</body>
<script src="assets/library/jquery341.js"></script>
<script>
//fails
  $('#test1').attr('style','border:1px solid black;');
// still works
  $('#test2').css({border:'1px solid black'});
</script>

</html>

lubber-de avatar Aug 31 '20 20:08 lubber-de

I found a nice fix that works right out of the box, but it's a hack. Just put this in your scripts, and jQuery's default setAttribute behavior does el.style.property="value" instead.

var setAttribute_ = Element.prototype.setAttribute;	// Save source of Elem.setAttribute funct

Element.prototype.setAttribute = function (attr, val) {
  if (attr.toLowerCase() !== 'style') {
    // console.log("set " + attr + "=`" + val + "` natively");
    setAttribute_.apply(this, [attr, val]);		// Call the saved native Elem.setAttribute funct
  } else { 
    // 'color:red; background-color:#ddd' -> el.style.color='red'; el.style.backgroundColor='#ddd';
    // console.log("set " + attr + "=`" + val + "` via setAttribute('style') polyfill");
    var arr = val.split(';').map( (el, index) => el.trim() );
    for (var i=0, tmp; i < arr.length; ++i) {
      if (! /:/.test(arr[i]) ) continue;		// Empty or wrong
      tmp = arr[i].split(':').map( (el, index) => el.trim() );
      this.style[ camelize(tmp[0]) ] = tmp[1];
      //console.log(camelize(tmp[0]) + ' = '+ tmp[1]);
    }
  }
}

function camelize(str) {
  return str
  .split('-')	// Parse 'my-long-word' to ['my', 'long', 'word']
  .map(
    // Coverts all elements to uppercase except first: ['my', 'long', 'word'] -> ['my', 'Long', 'Word']
    (word, index) => index == 0 ? word : word[0].toUpperCase() + word.slice(1)
  )
  .join(''); // Join ['my', 'Long', 'Word'] Π² 'myLongWord'
}

birthdaycorp avatar Dec 30 '21 08:12 birthdaycorp

Just hit the same issue, my dropdown icons cannot be displayed because CSP denies loading inline fonts. Any update on this?

Vringe avatar Nov 07 '23 16:11 Vringe

The dropdown icon shouldn't be an inline font. It is set as css property by default. Could you share a code snippet ? What csp meta Tag setting do you use?

lubber-de avatar Nov 07 '23 16:11 lubber-de

I mean the caret icons in the dropdown field:

https://github.com/fomantic/Fomantic-UI/blob/develop/dist/components/dropdown.css#L2013

Vringe avatar Nov 15 '23 09:11 Vringe

I mean the caret icons in the dropdown field:

develop/dist/components/dropdown.css#L2013

Thanks for clarification. In that case: Is adding "data:" to your font-src CSP restriction an option for you? https://security.stackexchange.com/questions/269804/is-it-safe-to-use-font-src-with-data-in-a-content-security-policy

lubber-de avatar Nov 15 '23 19:11 lubber-de

You may also fix this in your code by overriding the font-face back to the usual icon font

.ui.dropdown>.dropdown.icon {
    font-family: 'icons';
}

lubber-de avatar Nov 15 '23 19:11 lubber-de

Thank you. Actually I stumbled over the exact same article and I ended up adding data: to my CSP rule.

Vringe avatar Nov 16 '23 08:11 Vringe