Skip to content

Commit be325ef

Browse files
authored
Bump vulnerable transitive npm dependencies to patched versions (#944)
* Bump vulnerable transitive npm dependencies to patched versions - minimatch 9.0.3 → 9.0.7 (ReDoS, CVE) - picomatch 2.3.1 → 2.3.2 (ReDoS) - picomatch 4.0.3 → 4.0.4 (ReDoS) - yaml 1.10.2 → 1.10.3 (stack overflow via deeply nested collections) - yaml 2.8.2 → 2.8.3 (stack overflow via deeply nested collections) Addresses Dependabot alerts #148, #158, #159, #160, #161, #162. * Skip R loader test when rpy2 is incompatible with installed R version Recent rpy2 releases require R >= 4.1 (R_existsVarInFrame) / R >= 4.2 (R_getVar), but the CircleCI Ubuntu 20.04 images ship with R 3.6.3. The existing guard (pytest.importorskip) only skips when rpy2 is not installed, not when it is installed but incompatible with the R runtime. Add a lightweight import check so the test is skipped gracefully in that environment rather than erroring. * Install R from CRAN rather than Ubuntu apt default The Ubuntu 20.04 apt default (R 3.6.3) is incompatible with recent rpy2 releases which require R >= 4.1. Add the official CRAN Ubuntu repository so all Python version builds get R 4.x. Uses lsb_release to pick the correct codename (focal/jammy/noble) dynamically. * Fix hanging 'Install R' CI step by avoiding interactive add-apt-repository Replace `add-apt-repository` (which prompts for [ENTER]) with a direct non-interactive write to sources.list.d. Also removes the now-unnecessary apt update/upgrade and software-properties-common install. * Unpin dash, dash-bootstrap-components, and dash_daq - dash: unpinned for python > 3.7 (was <=2.18.2) - dash-bootstrap-components: unpinned for python > 3.0 (was <=1.7.1) - dash_daq: unpinned entirely (was <=0.5.0) * Fix dash_daq pin and werkzeug version detection for package updates - Pin dash_daq<=0.5.0 to avoid breakage with newer versions - Fix werkzeug.__version__ AttributeError in test_shutdown by falling back to importlib.metadata for newer werkzeug versions * Replace dash_daq with dbc.Switch and dbc.Input for Python 3+ dash_daq is incompatible with dash 4.x (removed component_loader). Replace BooleanSwitch with dbc.Switch and NumericInput with dbc.Input(type="number") for Python 3+, keeping dash_daq only for Python 2.7. Add switch.css to fix Bootstrap 4/5 style collisions. * Bump serialize-javascript to 7.0.5 and add handlebars 4.7.9 resolution - Bump serialize-javascript from 7.0.4 to 7.0.5 to fix CPU exhaustion DoS - Add handlebars 4.7.9 yarn resolution to fix JS injection via AST type confusion (CVE-2026-33937) and related vulnerabilities
1 parent e9ddc5c commit be325ef

10 files changed

Lines changed: 147 additions & 74 deletions

File tree

.circleci/config.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,9 @@ python: &python
145145
- run:
146146
name: Install R
147147
command: |
148+
wget -qO- https://cloud.r-project.org/bin/linux/ubuntu/marutter_pubkey.asc | sudo tee /etc/apt/trusted.gpg.d/cran_ubuntu_key.asc
149+
echo "deb https://cloud.r-project.org/bin/linux/ubuntu $(lsb_release -cs)-cran40/" | sudo tee /etc/apt/sources.list.d/cran-r.list
148150
sudo apt update
149-
sudo apt -y upgrade
150151
sudo apt -y install r-base
151152
if [ "${CIRCLE_JOB}" == "build_3_14" ]; then
152153
sudo apt -y install libtirpc-dev

dtale/dash_application/drilldown_modal.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
chart_builder_passthru,
1313
)
1414
from dtale.dash_application.exceptions import DtalePreventUpdate
15+
from dtale.dash_application.layout.layout import boolean_switch_prop
1516
from dtale.dash_application.layout.utils import (
1617
build_cols,
1718
build_option,
@@ -345,7 +346,7 @@ def load_drilldown_content(
345346
[
346347
State("drilldown-modal-{}".format(i), "is_open"),
347348
State("input-data", "data"),
348-
State("drilldown-toggle", "on"),
349+
State("drilldown-toggle", boolean_switch_prop()),
349350
],
350351
)(toggle_modal)
351352
dash_app.callback(
@@ -360,7 +361,7 @@ def load_drilldown_content(
360361
State("yaxis-data", "data"),
361362
State("map-input-data", "data"),
362363
State("chart-click-data-{}".format(i), "data"),
363-
State("drilldown-toggle", "on"),
364+
State("drilldown-toggle", boolean_switch_prop()),
364365
],
365366
)(update_click_data)
366367
dash_app.callback(
@@ -392,6 +393,6 @@ def load_drilldown_content(
392393
State("yaxis-data", "data"),
393394
State("map-input-data", "data"),
394395
State("chart-click-data-{}".format(i), "data"),
395-
State("drilldown-toggle", "on"),
396+
State("drilldown-toggle", boolean_switch_prop()),
396397
],
397398
)(load_drilldown_content)

dtale/dash_application/layout/layout.py

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import json
22

33
import dash_bootstrap_components as dbc
4-
import dash_daq as daq
54
import os
65
import plotly
76
from six import PY3
87

8+
if not PY3:
9+
import dash_daq as daq
10+
911
from dtale.dash_application import dcc, html
1012
import dtale.dash_application.components as dash_components
1113
import dtale.dash_application.custom_geojson as custom_geojson
@@ -924,6 +926,21 @@ def bootstrap_checkbox_prop():
924926
return "checked"
925927

926928

929+
def boolean_switch_prop():
930+
if parse_version(dbc.__version__) >= parse_version("1.0.0"):
931+
return "value"
932+
return "on"
933+
934+
935+
def build_boolean_switch(switch_id, on, color=None):
936+
if parse_version(dbc.__version__) >= parse_version("1.0.0"):
937+
return dbc.Switch(id=switch_id, value=on)
938+
kwargs = {"id": switch_id, "on": on}
939+
if color is not None:
940+
kwargs["color"] = color
941+
return daq.BooleanSwitch(**kwargs)
942+
943+
927944
def build_dropna(dropna, prop=None):
928945
if PY3:
929946
checkbox_kwargs = dict(
@@ -1031,7 +1048,7 @@ def build_funnel_inputs(inputs, df, group_options):
10311048
stacked_toggle = build_input(
10321049
"{}?".format(text("Stack")),
10331050
html.Div(
1034-
daq.BooleanSwitch(id="funnel-stack-toggle", on=False),
1051+
build_boolean_switch("funnel-stack-toggle", False),
10351052
className="toggle-wrapper",
10361053
),
10371054
id="funnel-stack-input",
@@ -2330,9 +2347,7 @@ def show_map_style(show):
23302347
build_input(
23312348
text("Drilldowns"),
23322349
html.Div(
2333-
daq.BooleanSwitch(
2334-
id="drilldown-toggle", on=False
2335-
),
2350+
build_boolean_switch("drilldown-toggle", False),
23362351
className="toggle-wrapper",
23372352
),
23382353
id="drilldown-input",
@@ -2412,8 +2427,9 @@ def show_map_style(show):
24122427
build_input(
24132428
text("Bins"),
24142429
[
2415-
daq.NumericInput(
2430+
dbc.Input(
24162431
id="bins-val-input",
2432+
type="number",
24172433
min=1,
24182434
max=30,
24192435
value=inputs.get("bins_val") or 5,
@@ -2481,9 +2497,7 @@ def show_map_style(show):
24812497
build_input(
24822498
text("Chart Per\nGroup"),
24832499
html.Div(
2484-
daq.BooleanSwitch(
2485-
id="cpg-toggle", on=inputs.get("cpg") or False
2486-
),
2500+
build_boolean_switch("cpg-toggle", inputs.get("cpg") or False),
24872501
className="toggle-wrapper",
24882502
),
24892503
id="cpg-input",
@@ -2493,9 +2507,7 @@ def show_map_style(show):
24932507
build_input(
24942508
text("Chart Per\nY"),
24952509
html.Div(
2496-
daq.BooleanSwitch(
2497-
id="cpy-toggle", on=inputs.get("cpy") or False
2498-
),
2510+
build_boolean_switch("cpy-toggle", inputs.get("cpy") or False),
24992511
className="toggle-wrapper",
25002512
),
25012513
id="cpy-input",
@@ -2618,8 +2630,8 @@ def show_map_style(show):
26182630
build_input(
26192631
text("Animate"),
26202632
html.Div(
2621-
daq.BooleanSwitch(
2622-
id="animate-toggle", on=inputs.get("animate") or False
2633+
build_boolean_switch(
2634+
"animate-toggle", inputs.get("animate") or False
26232635
),
26242636
className="toggle-wrapper",
26252637
),
@@ -2652,9 +2664,7 @@ def show_map_style(show):
26522664
top="120%",
26532665
),
26542666
html.Div(
2655-
daq.BooleanSwitch(
2656-
id="auto-load-toggle", on=True, color="green"
2657-
),
2667+
build_boolean_switch("auto-load-toggle", True, color="green"),
26582668
className="toggle-wrapper",
26592669
),
26602670
id="auto-load-input",

dtale/dash_application/views.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
show_input_handler,
4848
show_yaxis_ranges,
4949
bootstrap_checkbox_prop,
50+
boolean_switch_prop,
5051
get_num_cols,
5152
)
5253
from dtale.dash_application.layout.utils import show_style
@@ -66,11 +67,14 @@ class DtaleDash(dash.Dash):
6667

6768
def __init__(self, *args, **kwargs):
6869
server = kwargs.get("server")
69-
kwargs["external_stylesheets"] = [
70+
stylesheets = [
7071
"/dtale/static/css/main.css",
7172
"/dtale/static/css/dash.css",
7273
"/dtale/static/css/github_fork.css",
7374
]
75+
if PY3:
76+
stylesheets.append("/dtale/static/css/switch.css")
77+
kwargs["external_stylesheets"] = stylesheets
7478
kwargs["external_scripts"] = [
7579
"/dtale/static/dash/components_bundle.js",
7680
"/dtale/static/dash/custom_bundle.js",
@@ -781,7 +785,7 @@ def histogram_data_callback(
781785
Input("funnel-label-dropdown", "value"),
782786
Input("funnel-group-dropdown", "value"),
783787
Input("funnel-dropna-checkbox", bootstrap_checkbox_prop()),
784-
Input("funnel-stack-toggle", "on"),
788+
Input("funnel-stack-toggle", boolean_switch_prop()),
785789
],
786790
[State("data-tabs", "value")],
787791
)(funnel_callback)
@@ -868,13 +872,13 @@ def input_toggles(_ts, inputs, pathname):
868872
@dash_app.callback(
869873
Output("chart-input-data", "data"),
870874
[
871-
Input("cpg-toggle", "on"),
872-
Input("cpy-toggle", "on"),
875+
Input("cpg-toggle", boolean_switch_prop()),
876+
Input("cpy-toggle", boolean_switch_prop()),
873877
Input("barmode-dropdown", "value"),
874878
Input("barsort-dropdown", "value"),
875879
Input("top-bars", "value"),
876880
Input("colorscale-picker", "colorscale"),
877-
Input("animate-toggle", "on"),
881+
Input("animate-toggle", boolean_switch_prop()),
878882
Input("animate-by-dropdown", "value"),
879883
Input("trendline-dropdown", "value"),
880884
Input("yaxis-scale", "value"),
@@ -911,7 +915,9 @@ def chart_input_data(
911915
scale=scale,
912916
)
913917

914-
@dash_app.callback(Output("load-btn", "style"), [Input("auto-load-toggle", "on")])
918+
@dash_app.callback(
919+
Output("load-btn", "style"), [Input("auto-load-toggle", boolean_switch_prop())]
920+
)
915921
def load_style(auto_load):
916922
return dict(display="block" if not auto_load else "none")
917923

@@ -987,7 +993,7 @@ def collapse_cleaners_input(n, is_open):
987993
State("pareto-input-data", "data"),
988994
State("histogram-input-data", "data"),
989995
State("last-chart-input-data", "data"),
990-
State("auto-load-toggle", "on"),
996+
State("auto-load-toggle", boolean_switch_prop()),
991997
State("load-clicks", "data"),
992998
State("extended-aggregations", "data"),
993999
],

dtale/static/css/switch.css

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Bootstrap 5 Switch overrides for dtale toggle-wrappers.
3+
* Fixes conflicts between Bootstrap 4 .form-check-input rules in main.css
4+
* and Bootstrap 5's .form-switch component used by dbc.Switch.
5+
*/
6+
7+
/* Reset the form-check container within toggle-wrapper */
8+
.toggle-wrapper .form-check.form-switch {
9+
display: flex;
10+
align-items: center;
11+
justify-content: center;
12+
padding-left: 0;
13+
margin-bottom: 0;
14+
min-height: auto;
15+
height: 100%;
16+
}
17+
18+
/* Override BS4 .form-check-input { position: absolute } from main.css */
19+
.toggle-wrapper .form-check.form-switch .form-check-input {
20+
position: relative;
21+
float: none;
22+
flex-shrink: 0;
23+
width: 2em;
24+
height: 1em;
25+
margin-left: 0;
26+
margin-top: 0;
27+
vertical-align: top;
28+
appearance: none;
29+
-webkit-appearance: none;
30+
background-color: #fff;
31+
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");
32+
background-repeat: no-repeat;
33+
background-position: left center;
34+
background-size: contain;
35+
border: 1px solid #dee2e6;
36+
border-radius: 2em;
37+
cursor: pointer;
38+
transition: background-position 0.15s ease-in-out;
39+
print-color-adjust: exact;
40+
}
41+
42+
.toggle-wrapper .form-check.form-switch .form-check-input:checked {
43+
background-color: #0d6efd;
44+
border-color: #0d6efd;
45+
background-position: right center;
46+
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e");
47+
}
48+
49+
.toggle-wrapper .form-check.form-switch .form-check-input:focus {
50+
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e");
51+
border-color: #86b7fe;
52+
outline: 0;
53+
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
54+
}
55+
56+
.toggle-wrapper .form-check.form-switch .form-check-input:disabled {
57+
pointer-events: none;
58+
filter: none;
59+
opacity: 0.5;
60+
}
61+
62+
/* Hide the empty label generated by dbc.Switch */
63+
.toggle-wrapper .form-check.form-switch .form-check-label {
64+
padding-left: 0;
65+
display: none;
66+
}

frontend/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@
107107
"resolve": "1.22.11",
108108
"sass": "1.98.0",
109109
"sass-loader": "16.0.7",
110-
"serialize-javascript": "7.0.4",
110+
"serialize-javascript": "7.0.5",
111111
"style-loader": "4.0.0",
112112
"terser": "5.46.0",
113113
"terser-webpack-plugin": "5.4.0",
@@ -179,6 +179,7 @@
179179
"d3-color": "3.1.0",
180180
"lodash": "4.17.23",
181181
"lodash-es": "4.17.23",
182-
"micromatch": "4.0.8"
182+
"micromatch": "4.0.8",
183+
"handlebars": "4.7.9"
183184
}
184185
}

0 commit comments

Comments
 (0)