Discussions » Development

Setting CSS stylesheet when Subresource Integrity is enabled (keyword: GM_setStyle)

§
Posted: 28.05.2023
Edited: 28.05.2023

Hello,

I've had an idea as to how to apply CSS style when Subresource Integrity is enabled.

I've wrote this code as a proof of concept:

jsonStyle = {
  // CSS stylesheet converted into JSON
  // https://transform.tools/css-to-js
}

list = Object.keys(jsonStyle);

for (let i = 0; i < list.length; i++) {
  for (const element of document.querySelectorAll(`${list[i]}`)) {
    element.style = JSON.stringify(jsonStyle[`${list[i]}`])
      .slice(2)
      .replace('"}','')
      .replace(/['"]+/g, '')
      .replace(/[',]+/g, ';');
    //console.log(list[i])
    //console.log(element.style)
  }
}

This code doesn't work with pseudo classes yet.

Please help me to improved this, so we have a library that is more robust than GM_setStyle.

§
Posted: 01.06.2023
Edited: 01.06.2023

As per your link's suggestion, you can obtain the nonce value in securitypolicyviolation event.

You can use fetch API to trigger the error to obtain nonceValue

Once you have the value, you can set it when you create a style element. (styleElement.setAttribute('nonce', nonceValue))


    let nonceValue = null;
    window.addEventListener("securitypolicyviolation", (e) => {
        if (nonceValue === null) {
            let match = /'nonce-([^']+)'/.exec(e.originalPolicy);
            nonceValue = match ? match[1] : '';
        }
    }, { passive: true });

    try {
        fetch('about:').catch(e => { })
    } catch (e) { }

No one will do the stupid thing like your code.

  1. performance is poor - N selectors N querySelectorAll calls; terrible reflow and repaint triggering.
  2. need to do everytime an element is inserted
  3. you are breaking the entire concept of css rules. all your style settings will be the 2nd top priority level and you cannot make use of css selectors to change the styles. (1st top priority level is style.prop = 'xxx !important';)
  4. it is just stupid
    const addStyle = (cssText) => {
        let style = document.createElement('style');
        style.setAttribute('nonce', nonceValue);
        style.textContent = cssText;
        document.head.appendChild(style);
    }
§
Posted: 01.06.2023

Thank you. I already did something similar.


function setNonceUponCSP() {
  window.addEventListener("securitypolicyviolation", (e) => {
    cssStylesheet = document.getElementById(namespace);
    let nonceValue = e.originalPolicy.match(/'nonce-(.*?)'/)[1];
    cssStylesheet.setAttribute('nonce', nonceValue); 
    // Reload stylesheet
    textContent = cssStylesheet.textContent;
    cssStylesheet.textContent = null;
    cssStylesheet.textContent = textContent;
  }, { passive : true, });
}

The problem is that not all websites provide a 'nonce', hence I think that the proposed idea (CSS as JSON) is a good solution for the near future.

§
Posted: 03.06.2023
Edited: 03.06.2023

just found that GM_addStyle can by pass CSP in TamperMonkey

// ==UserScript==
// @name         New Userscript
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  try to take over the world!
// @author       You
// @match        https://mastodon.tedomum.net/@tedomum.rss
// @icon         https://www.google.com/s2/favicons?sz=64&domain=tedomum.net
// @grant        GM_addStyle
// ==/UserScript==

(function() {
    'use strict';

    GM_addStyle("body{color:red;}")

    // Your code here...
})();
§
Posted: 05.06.2023

Interesting.

It does bypass CSP even without GM_addStyle.

I'll be sending another issue report to Greasemonkey for Falkon about it.

Test page:
https://archlinux.org/feeds/news/

Userscript:
https://greasyfork.org/en/scripts/465932-newspaper

Thank you, CY Fung

Post reply

Sign in to post a reply.