From 9982e4bff38960180176f32a24f0ee30a6063b85 Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Fri, 14 Aug 2026 19:34:37 +0000
Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=94=92=20Fix=20XSS=20vulnerability=20?=
=?UTF-8?q?in=20nv-modal?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: manupawickramasinghe <73810867+manupawickramasinghe@users.noreply.github.com>
---
dashboard/package-lock.json | 20 ++++++++++++++++++++
dashboard/package.json | 2 ++
dashboard/src/components/nv-modal.ts | 3 ++-
3 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/dashboard/package-lock.json b/dashboard/package-lock.json
index 1b24bf152e..da02b12dcb 100644
--- a/dashboard/package-lock.json
+++ b/dashboard/package-lock.json
@@ -9,6 +9,8 @@
"version": "0.1.0",
"dependencies": {
"@preact/signals-core": "^1.8.0",
+ "@types/dompurify": "^3.0.5",
+ "dompurify": "^3.4.13",
"lit": "^3.2.1",
"workbox-window": "^7.4.0"
},
@@ -2529,6 +2531,15 @@
"sourcemap-codec": "^1.4.8"
}
},
+ "node_modules/@types/dompurify": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz",
+ "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/trusted-types": "*"
+ }
+ },
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
@@ -3212,6 +3223,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/dompurify": {
+ "version": "3.4.13",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.13.tgz",
+ "integrity": "sha512-2vmYIoqjze2d+kakP8S/nS5shfsl587kzwEjcGlTdiksUVgFHnFCsLYDVj/JNqJVOQZGSYBTmuycv0PodwmnMQ==",
+ "license": "(MPL-2.0 OR Apache-2.0)",
+ "optionalDependencies": {
+ "@types/trusted-types": "^2.0.7"
+ }
+ },
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
diff --git a/dashboard/package.json b/dashboard/package.json
index e99f9ae45e..c9f61076e7 100644
--- a/dashboard/package.json
+++ b/dashboard/package.json
@@ -16,6 +16,8 @@
},
"dependencies": {
"@preact/signals-core": "^1.8.0",
+ "@types/dompurify": "^3.0.5",
+ "dompurify": "^3.4.13",
"lit": "^3.2.1",
"workbox-window": "^7.4.0"
},
diff --git a/dashboard/src/components/nv-modal.ts b/dashboard/src/components/nv-modal.ts
index 23b4c65750..cc2edcf038 100644
--- a/dashboard/src/components/nv-modal.ts
+++ b/dashboard/src/components/nv-modal.ts
@@ -1,6 +1,7 @@
/* Modal dialog — opened via window.dispatchEvent('nv-modal', { title, body, buttons }). */
import { LitElement, html, css } from 'lit';
import { customElement, state } from 'lit/decorators.js';
+import DOMPurify from 'dompurify';
interface ModalButton {
label: string;
@@ -137,7 +138,7 @@ export class NvModal extends LitElement {
${this.mTitle}
-
+
${this.buttons.map((b) => html`
From ab1ca93d095bf99f752b85a7cda6a09f2760fc84 Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Fri, 14 Aug 2026 20:42:51 +0000
Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=94=92=20Fix=20XSS=20vulnerability=20?=
=?UTF-8?q?in=20nv-modal=20&=20a11y=20improvements?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: manupawickramasinghe <73810867+manupawickramasinghe@users.noreply.github.com>
---
dashboard/src/components/nv-app-store.ts | 8 ++++----
dashboard/src/components/nv-console.ts | 2 +-
dashboard/src/components/nv-ghost-murmur.ts | 6 +++---
dashboard/src/components/nv-inspector.ts | 2 +-
dashboard/src/components/nv-settings-drawer.ts | 4 ++--
5 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/dashboard/src/components/nv-app-store.ts b/dashboard/src/components/nv-app-store.ts
index f6b90f9bc7..e50dbfb285 100644
--- a/dashboard/src/components/nv-app-store.ts
+++ b/dashboard/src/components/nv-app-store.ts
@@ -97,7 +97,7 @@ export class NvAppStore extends LitElement {
.chip .swatch {
width: 7px; height: 7px; border-radius: 50%;
}
- .chip .count { color: var(--ink-3); font-size: 10px; }
+ .chip .count { color: var(--ink-2); font-size: 10px; }
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
@@ -139,7 +139,7 @@ export class NvAppStore extends LitElement {
}
.badge {
padding: 1px 6px; border-radius: 4px;
- background: var(--bg-3); color: var(--ink-3);
+ background: var(--bg-3); color: var(--ink-2);
border: 1px solid var(--line);
}
.badge.cat { color: var(--accent); border-color: oklch(0.78 0.14 70 / 0.3); }
@@ -149,7 +149,7 @@ export class NvAppStore extends LitElement {
.badge.budget { color: var(--accent-2); border-color: oklch(0.78 0.12 195 / 0.3); }
.badge.rt-running { color: var(--ok); border-color: oklch(0.78 0.14 145 / 0.5); background: oklch(0.78 0.14 145 / 0.08); }
.badge.rt-simulated { color: var(--accent); border-color: oklch(0.78 0.14 70 / 0.5); background: oklch(0.78 0.14 70 / 0.08); }
- .badge.rt-mesh-only { color: var(--ink-3); border-color: var(--line); }
+ .badge.rt-mesh-only { color: var(--ink-2); border-color: var(--line); }
.events-feed {
background: var(--bg-2);
border: 1px solid var(--line);
@@ -389,7 +389,7 @@ export class NvAppStore extends LitElement {
${app.crate}
${evCount > 0 ? html`⚡ ${evCount} ev` : ''}
this.toggle(app)}>
diff --git a/dashboard/src/components/nv-console.ts b/dashboard/src/components/nv-console.ts
index 1888ece742..6b791a4297 100644
--- a/dashboard/src/components/nv-console.ts
+++ b/dashboard/src/components/nv-console.ts
@@ -66,7 +66,7 @@ export class NvConsole extends LitElement {
border-left: 2px solid transparent;
}
.line:hover { background: var(--bg-1); }
- .ts { color: var(--ink-4); font-size: 10.5px; padding-top: 1px; }
+ .ts { color: var(--ink-2); font-size: 10.5px; padding-top: 1px; }
.lvl {
font-size: 10px; font-weight: 600;
text-transform: uppercase; letter-spacing: 0.04em; padding-top: 1px;
diff --git a/dashboard/src/components/nv-ghost-murmur.ts b/dashboard/src/components/nv-ghost-murmur.ts
index aebf31cdaf..78e0b1296b 100644
--- a/dashboard/src/components/nv-ghost-murmur.ts
+++ b/dashboard/src/components/nv-ghost-murmur.ts
@@ -394,7 +394,7 @@ export class NvGhostMurmur extends LitElement {
Distance from sensor
${this.formatDistance(this.distanceM)}
- { this.distanceM = Math.pow(10, +(e.target as HTMLInputElement).value); }} />
@@ -407,7 +407,7 @@ export class NvGhostMurmur extends LitElement {
Heart dipole moment
${m.toExponential(2)} A·m²
- { this.momentLog10 = +(e.target as HTMLInputElement).value; }} />
@@ -536,7 +536,7 @@ export class NvGhostMurmur extends LitElement {
RuView's three-tier mesh — what is actually buildable
- ┌──────────────────────────┐
+
┌──────────────────────────┐
│ Tier 3 — NV-diamond │ Range: 0.1–2 m (lab)
│ magnetometer ring │ Status: nvsim simulator only
│ (close-confirm) │ Hardware: $$$ (≥$8k DNV-B1)
diff --git a/dashboard/src/components/nv-inspector.ts b/dashboard/src/components/nv-inspector.ts
index a0d7495426..342ca7e882 100644
--- a/dashboard/src/components/nv-inspector.ts
+++ b/dashboard/src/components/nv-inspector.ts
@@ -423,7 +423,7 @@ export class NvInspector extends LitElement {
role="tab" aria-selected=${this.tab === 'witness'}
@click=${() => this.tab = 'witness'}>Witness
-
+
${this.renderHeader()}
${this.tab === 'signal' ? this.renderSignalTab()
: this.tab === 'frame' ? this.renderFrameTab()
diff --git a/dashboard/src/components/nv-settings-drawer.ts b/dashboard/src/components/nv-settings-drawer.ts
index 92544147cc..85b69d3477 100644
--- a/dashboard/src/components/nv-settings-drawer.ts
+++ b/dashboard/src/components/nv-settings-drawer.ts
@@ -172,7 +172,7 @@ export class NvSettingsDrawer extends LitElement {
Stops the rotating diamond, animated field lines, and chart easing. Auto-on if your system has the prefers-reduced-motion preference set.
motionReduced.value = !motionReduced.value}>
@@ -185,7 +185,7 @@ export class NvSettingsDrawer extends LitElement {
When you change a Tunables slider or load a new scene, push the change to the worker without a manual restart.
autoUpdate.value = !autoUpdate.value}>
From 3386f1fcb43531e4017d86eb71c85db6498de985 Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Fri, 14 Aug 2026 21:12:23 +0000
Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=94=92=20Fix=20additional=20a11y=20co?=
=?UTF-8?q?lor=20contrast=20violations?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: manupawickramasinghe <73810867+manupawickramasinghe@users.noreply.github.com>
---
dashboard/src/components/nv-onboarding.ts | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/dashboard/src/components/nv-onboarding.ts b/dashboard/src/components/nv-onboarding.ts
index 29e9cb1ca7..04440d3268 100644
--- a/dashboard/src/components/nv-onboarding.ts
+++ b/dashboard/src/components/nv-onboarding.ts
@@ -33,7 +33,7 @@ const STEPS: TourStep[] = [
This 60-second tour walks you through the four panels, the App Store,
the Ghost Murmur research view, and the determinism contract that
makes nvsim distinctive.
-
+
Press Esc any time to skip. You can replay this tour from
Settings → Help.
`,
cta: { label: 'Start the tour →' },
@@ -224,7 +224,7 @@ export class NvOnboarding extends LitElement {
.h .step-label {
font-family: var(--mono);
font-size: 10.5px;
- color: var(--ink-3);
+ color: var(--ink-2);
margin-top: 4px;
text-transform: uppercase;
letter-spacing: 0.06em;
@@ -299,7 +299,7 @@ export class NvOnboarding extends LitElement {
.progress-label {
font-family: var(--mono);
font-size: 10px;
- color: var(--ink-3);
+ color: var(--ink-2);
}
button.primary, button.ghost {
padding: 9px 16px;