Skip to content

fix(ui): bump jQuery and Bootstrap, fix modal XSS - #177

Merged
skamboj merged 4 commits into
bloomberg:masterfrom
skamboj:ui-deps
Oct 5, 2026
Merged

skamboj merged 4 commits into
bloomberg:masterfrom
skamboj:ui-deps

Conversation

@skamboj

@skamboj skamboj commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Why

The dashboard bundles two front-end libraries from 2016, jQuery 3.1.0 and Bootstrap 3.3.7, and both have published CVEs (counts from OSV.dev):

Library Vendored Known CVEs This PR Known CVEs
jQuery 3.1.0 CVE-2019-11358, CVE-2020-11022, CVE-2020-11023 3.7.1 none
Bootstrap 3.3.7 CVE-2016-10735, CVE-2018-14040, CVE-2018-14042, CVE-2018-20676, CVE-2018-20677, CVE-2019-8331, CVE-2024-6485 3.4.1 CVE-2024-6485, CVE-2025-1647

The image scan in #175 / #176 didn't catch these, because Trivy only finds JavaScript libraries through package manifests, and these files are copied in directly.

#171 and #172 tried to fix two of these CVEs by applying upstream commits by hand, but neither works:

  • Both patch the unminified jquery.js and bootstrap.js, while static/index.html loads the .min.js builds. The browser would run exactly the same code after merging.
  • CVE-2015-9251 (Fix potentially vulnerable cloned function #172) doesn't affect jQuery 3.1.0 in the first place: that release already ships the upstream fix (the s.contents.script = false prefilter, gh-2432).

Upgrading the vendored libraries fixes all of these properly and replaces both PRs.

While checking the UI against the same threat model as #174 (a pod that matches the label selector and lies in its /check response), I also found an XSS that no library version fixes:

  • /check_all embeds every peer's response verbatim.
  • The UI wrote that data into the page with .html().
  • Node IDs, which come from peer-reported podResults keys, went into the modal title.
  • prepareJSON() built the modal body as an HTML string around JSON.stringify() output, which doesn't escape < or >.

So any such pod could run script in the browser of whoever opens the dashboard. #174 stopped a lying peer from crashing /cluster_health; this stops it from attacking dashboard viewers.

After this PR, the UI has no reachable known CVEs from its libraries, and peer-supplied strings render as text.

Summary

  • fix(ui): upgrade vendored jQuery to 3.7.1
    • 3.7.1 is the latest 3.x. jQuery 4 isn't an option because Bootstrap 3's JavaScript refuses to run on it.
  • fix(ui): upgrade vendored Bootstrap to 3.4.1
    • 3.4.1 is the final 3.x release.
    • Its two remaining advisories are in the Button plugin's data-loading-text (CVE-2024-6485) and in Popover/Tooltip (CVE-2025-1647). The UI uses neither.
  • Both upgrades:
    • The files are the unmodified dist/ of the npm packages, in the same layout as before (unminified, minified, slim and source maps for jQuery; css, fonts and js for Bootstrap).
    • Each file matches the SRI hash published on releases.jquery.com and getbootstrap.com.
    • The old version directories are removed, and static/index.html points at the new paths.
  • fix(ui): escape peer data rendered in modals
    • prepareJSON() now returns a <code> element filled with .text(), and callers attach it with .empty().append().
    • Every #modal-title write uses .text().
    • The only remaining .html() call is the heatmap <img>, whose URL is built only from Number() values and Date.now().
  • chore(version): bump to v3.11.5 / chart 1.1.5 (v3.11.4 is already tagged).

Supersedes #171 and #172.

Test plan

  • SRI hashes of all 7 published artifacts (jQuery js/min/slim/slim.min; Bootstrap bootstrap.min.css/bootstrap-theme.min.css/bootstrap.min.js) match the official values.
  • Browser end-to-end check (Playwright driving Chrome) against goldpinger installed with the Helm chart on a local Kubernetes cluster, before vs after the library upgrade, 3 runs each:
    • Library versions loaded: jQuery 3.7.1, Bootstrap modal and collapse 3.4.1.
    • Graph render, Data modal, Heatmap modal with threshold refresh, Escape to close, node click, reload, and mobile navbar collapse all behave the same as before.
    • No page errors and no failed requests.
    • The pixel diff of screenshots changes only data-dependent content (pod names, timings).
  • XSS end-to-end check with a real hostile peer: an nginx pod labelled app.kubernetes.io/name=goldpinger whose /check puts markup in podResults keys and in an error value.
    • Without the escaping commit: all three payloads execute, via the node-click title, a key in the Data modal, and the error field in the Data modal.
    • With it: none execute, and the payloads appear as literal text.
    • The Data modal body is exactly JSON.stringify(data, undefined, 4), and real pod modals keep their white-space: pre formatting.
  • Library upgrade deployed to a 3-node Kubernetes cluster with the Helm chart and reviewed in a browser.

jQuery 3.1.0 is affected by CVE-2019-11358 (prototype pollution in
jQuery.extend, fixed in 3.4.0) and CVE-2020-11022/CVE-2020-11023 (XSS
via htmlPrefilter, fixed in 3.5.0). 3.7.1 is the latest 3.x release
and has no known advisories. jQuery 4 is not an option because
Bootstrap 3 refuses to run on it.

The files are the unmodified dist/ of the [email protected] npm package
and match the SRI hashes published on releases.jquery.com.

Signed-off-by: Sachin Kamboj <[email protected]>
Bootstrap 3.3.7 is affected by CVE-2016-10735, CVE-2018-14040,
CVE-2018-14042, CVE-2018-20676, CVE-2018-20677 and CVE-2019-8331, all
fixed by 3.4.1. 3.4.1 is the final 3.x release; it still carries
CVE-2024-6485 (button data-loading-text) and CVE-2025-1647 (popover
and tooltip), neither of which the UI uses.

The files are the unmodified dist/ of the [email protected] npm package
and match the SRI hashes published on getbootstrap.com.

Signed-off-by: Sachin Kamboj <[email protected]>
check_all embeds every peer's /check response verbatim, and the UI
wrote that data into the page with .html(): node ids, which come from
peer-reported podResults keys, went into the modal title, and
prepareJSON() concatenated JSON.stringify() output, which does not
escape < or >, into an HTML string for the modal body. Any pod that
matches the goldpinger label selector could therefore run script in
the browser of whoever opens the dashboard - the same lying-peer
threat model that bloomberg#174 addressed for /cluster_health.

prepareJSON() now returns a <code> element filled via .text(), which
callers attach with .empty().append(), and modal titles are set with
.text(). Legitimate data renders exactly as before.

Signed-off-by: Sachin Kamboj <[email protected]>
Signed-off-by: Sachin Kamboj <[email protected]>
@skamboj
skamboj merged commit 8634819 into bloomberg:master Oct 5, 2026
3 checks passed
@skamboj
skamboj deleted the ui-deps branch October 5, 2026 15:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant