dreamRs / shinyWidgets

shinyWidgets : Extend widgets available in shiny
https://dreamrs.github.io/shinyWidgets/
GNU General Public License v3.0
830 stars 153 forks source link

bootstrap-switch dependency not actively maintained #529

Open hedsnz opened 2 years ago

hedsnz commented 2 years ago

There is an unpatched potential XSS vulnerability in bootstrap-switch source, see for reference https://github.com/bttstrp/bootstrap-switch/pull/730.

The upstream appears not to be actively maintained (https://github.com/Bttstrp/bootstrap-switch/issues/717), so may not be sustainable to continue to use. Is it possible to remove this dependency and rely on the native Bootstrap switch itself? (Although I'm not sure whether this works in Bootstrap 3.) Happy to contribute to any work toward this.

hedsnz commented 2 years ago

MRE of XSS:

library(shiny)
library(shinyWidgets)

ui <- fluidPage(
    textInput("onText", "Label for 'ON'", value = '<script>alert("xss")</script>'),
    uiOutput("switch")
)

server <- function(input, output) {
    output$switch <- renderUI({
        switchInput(inputId = "somevalue", onLabel = input$onText)
    })
}

shinyApp(ui, server)

If the upstream cannot be patched, then an interim measure may be to sanitise the input on switchInput, for example:

escape_html <- function(string) {
    string <- gsub("&", "&amp;", string)
    string <- gsub("<", "&lt;", string)
    string <- gsub(">", "&gt;", string)
    string <- gsub('"', "&quot;", string)
    string <- gsub("'", "&#x27;", string)
    string
}

switchInput <- function(...) {
    # ...
    switchProps <- dropNulls(list(id = inputId, type = "checkbox", 
        class = "sw-switchInput", `data-input-id` = inputId, 
        # Prevent XSS by sanitising user input here. 
        # Affects onText(), offText() and labelText() functions from bootstrap-switch.js.
        `data-on-text` = escape_html(onLabel), `data-off-text` = escape_html(offLabel), 
        `data-label-text` = escape_html(label), `data-on-color` = onStatus, 
        `data-off-color` = offStatus, `data-label-width` = labelWidth, 
        `data-handle-width` = handleWidth, disabled = if (!disabled) NULL else disabled, 
        `data-size` = if (size == "default") "" else size))
    # ...
}