glin / reactable

Interactive data tables for R
https://glin.github.io/reactable
Other
612 stars 79 forks source link

Stylesheet and CSP headers #332

Closed rifius closed 11 months ago

rifius commented 11 months ago

Some of my dynamic and static pages using reactable:: are served by a web server behind a reverse proxy of which I have no control. The reverse proxy adds some Content-Security-Policy headers to every page being served. In particular, the CSP header for css style elements does not allow instantiation of embedded stylesheets as data, for example

<link href="data:text/css,blah.blah.blah..."; rel="stylesheet">

as this is considered an insecure source scheme.

reactable:: does precisely that, it inserts a <link rel="stylesheet"> tag that begins with

<link href="data:text/css,%2EReactable%7B%2Dwebkit%2Dbox%2Dorient%3Avertical .....

If the stylesheet is rejected by the browser (due to the CSP), the whole table is rendered garbled, in a single column.

A MWE can be easily constructed by knitting to HTML an rmarkdown document with just

library(reactable)
reactable(mtcars)

If such document is served by a server that adds the header

Content-Security-Policy default-src 'self'; style-src 'self' 'unsafe-inline'

the table is wrongly rendered, while if the document is opened as local content or from a source not adding CSP headers rendering is OK.

Screenshot from 2023-07-21 15-02-42

I'd like to know if there is anything I can do while creating these pages such that the data:text/css,.... is included in some other way, e.g. via an inline <style> section or from external source, etc. that would be considered more secure by the sys admins.

Thanks for an otherwise fantastic package !

glin commented 11 months ago

Hi, reactable now uses an external stylesheet instead of injecting the base styles as of a few versions ago, I think v0.4.0 (it could've been documented better). In recent versions, the link tag should look more like this:

<link href="lib/reactable-0.4.4/reactable.css" rel="stylesheet" />

Which version of the package are you running?

However, even with v0.4.0, theming via reactableTheme() will still inject styles at runtime and probably run into CSP issues. In that case, you can try the new static rendering feature to generate a stylesheet for those themes in R rather than injecting. That may get around the issues, but I'm not 100% sure about that. We could also add a way to configure a nonce to add to the injected styles.

rifius commented 11 months ago

Hi @glin , That's odd. I'm using v0.4.4 of both reactable and reactR, and the injected string is there. I've searched the whole html file and there is nothing with reactable.css (except a single appearance in a compacted <script> tag right after the offending <link whose relevant part reads like:

{
  var o = r.value, a = o.href.substring(o.href.lastIndexOf("/") + 1);
  if ("stylesheet" === o.rel && "reactable.css" === a) {
    e = o.parentElement, t = o;
    break
  }
}

-- it is a for loop, prettifying is mine)

Static rendering fails for some reason and falls back to dynamic rendering. Attaching full MWE markdown and output, with static = TRUE and session info.

TR.Rmd.gz TR.html.gz

rifius commented 11 months ago

In the long term, a nonce, an embedded <style>...</style> or an external .css could be acceptable solutions as per my sys admin. Though I think managing nonces could become problematic.

Cheers,

rifius commented 11 months ago

Hi @glin . Found the issue, is not a reactable problem, but a feature of rmarkdown when producing "self contained" HTML outputs (the default). The docs say:

    self_contained
        Produce a standalone HTML file with no external dependencies, using data: URIs to incorporate the 
        contents of linked scripts, stylesheets, images, and videos. 

A 'self_contained: no` in the preamble puts all dependencies in a folder, and that works fine with the current CSP. Thanks, and sorry for wasting your time. Cheers,

glin commented 11 months ago

Ahh gotcha, it makes sense now. Well thanks for bringing it up anyway. It'll be useful to know if anyone else hits this in the future.