# Stored XSS in the network visualizer via unescaped entity names

**URL:** <https://openziti.discourse.group/t/stored-xss-in-the-network-visualizer-via-unescaped-entity-names/6146>\
**Category:** Security Adivsories\
**Created:** [September 29, 2026, 9:09pm UTC](https://openziti.discourse.group/t/stored-xss-in-the-network-visualizer-via-unescaped-entity-names/6146 "2026-09-29T21:09:07Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![rgalletto](https://yyz2.discourse-cdn.com/free1/user_avatar/openziti.discourse.group/rgalletto/32/1508_2.png) [@rgalletto](https://openziti.discourse.group/u/rgalletto)\
**Post date:** [September 29, 2026, 9:09pm UTC](https://openziti.discourse.group/t/stored-xss-in-the-network-visualizer-via-unescaped-entity-names/6146/1 "2026-09-29T21:09:07Z")

</div>

## Summary

The ZAC network visualizer renders some node labels and hover tooltips by writing controller entity data straight into the DOM, without HTML-encoding it. An entity whose name or role attributes contain HTML/script markup is rendered as markup, resulting in stored cross-site scripting in the console when an operator opens the visualizer.

## Affected Component

- **Package:** `openziti/ziti-console` (Ziti Admin Console) and the `@openziti/ziti-console-lib` shared library
- **File:** `projects/ziti-console-lib/src/lib/features/visualizer/network-visualizer/network-visualizer.component.ts`
- **Sinks:** node label rendering via d3 `.html()`, and the tree hover tooltip via `element.innerHTML`

## Vulnerability Class

- **Primary:** CWE-79 — Improper Neutralization of Input During Web Page Generation (Cross-site Scripting), stored/DOM-based

## Attacker Model

Requires an actor able to create or name Ziti entities (identities, services, routers) or set their role attributes. This is **not** an unauthenticated remote attack — the payload is stored in entity data and executes later, in the browser session of an operator who views it.

## Preconditions

- An entity name or role attribute contains HTML/script markup.
- An operator opens the network visualizer and the affected node label/tooltip is rendered.

## Root Cause

The visualizer writes entity data into the DOM through d3's `.html()` and `element.innerHTML`, which bypass Angular's built-in template sanitization. Controller-supplied names and attributes were interpolated into that markup without encoding, so any markup they contain is parsed and executed rather than displayed as text. (The identity-service-path tooltip already HTML-encodes its values and is not affected.)

## Impact

Stored XSS executing in the console's origin, in the session of the viewing operator. Conservative severity reflects the authenticated precondition and the required operator interaction.

- **CVSS v3.1:** 5.4 (Medium)
- **Vector:** `AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N`

**Not impacted:** no unauthenticated access, no RCE, no controller auth bypass; exploitation depends on an operator opening the visualizer.

## Package

`openziti/ziti-console` (app-ziti-console) and `@openziti/ziti-console-lib`

## Affected Versions

- app-ziti-console `< 4.5.4`
- ziti-console-lib `< 1.5.4`

## Patched Versions

- app-ziti-console `4.5.4`
- ziti-console-lib `1.5.4`

## Remediation

Upgrade to the patched versions. The fix HTML-encodes entity-supplied strings before inserting them at these sinks (via a shared `escapeHtml` helper) so they always render as text.

## References

- GHSA: [https://github.com/openziti/ziti-console/security/advisories/GHSA-rf2c-2p5r-7g29](https://github.com/openziti/ziti-console/security/advisories/GHSA-rf2c-2p5r-7g29)
- CVE: (pending)

## Credits

Reported by sajdakabir.
