{"id":"GHSA-3c48-9r95-hqhr","revision":null,"records":[{"revision":"ced1bdfa89df45539b82870d32f4d042","observedAt":"2026-10-24T13:19:54.901+00:00","statement":{"source":"osv","id":"RUSTSEC-2026-0345","aliases":["GHSA-3c48-9r95-hqhr"],"title":"Untrusted labels can inject SVG attributes, CSS values, and terminal escape sequences","description":"Affected versions of `kuva` wrote data-derived strings straight into rendered\noutput without escaping them for the target format. Applications that plot\nuntrusted input (column headers, category and group names, series labels)\nwere affected in three places.\n\n**1. SVG attribute injection / stored XSS (CWE-79).** When SVG interactivity\nwas enabled via `Layout::with_interactive`, group and category labels were\nformatted directly into `data-group` and `data-x` attributes through the raw\n`Primitive::GroupStart.extra_attrs` string, which the SVG backend emitted\nverbatim. The backend's escaping helper covered text nodes and `<title>` but\nnot attributes. A label containing a double quote could close the attribute\nand add another, for example an event handler. Opening the resulting SVG in a\nbrowser, or inlining it into an HTML page, executed the injected script.\nReachable from scatter, line, bar, and strip plots, and from legends.\n\n**2. Unvalidated CSS and style attribute values (CWE-74).** `Color::Css`\nwrote arbitrary strings into `fill` and `stroke`, `stroke-dasharray` was\nemitted raw from the public dash-pattern builders, and the SVG root\n`font-family` and `fill` plus the background rectangle `fill` were written\nraw from layout and theme configuration. Applications that forwarded\nuser-supplied style values into the plotting API could have those values\nbreak out of the attribute they were written into.\n\n**3. Terminal escape sequence injection (CWE-150).** `TerminalBackend` stored\nlabel characters unfiltered and emitted each one unconditionally when\nproducing its ANSI string. Control characters in a label, including ESC, OSC\nsequences, and carriage returns, were written straight to the operator's\nterminal. Depending on the emulator this allows forged or misleading output,\nwindow title and clipboard manipulation, and reaching terminal emulator\nvulnerabilities that are triggered by escape sequences.\n\nThe fix routes every data-derived string through format-appropriate\nescaping. A single `escape_attr` helper XML-escapes `&`, `<`, `>`, `\"`, and\n`'`, and is applied at each raw-attribute site: the `extra_attrs`\ninterpolations, the `Color::Css` catch-all, and the SVG backend's\n`stroke-dasharray`, `font-family`, and background `fill` writes.\n`TerminalBackend::set_char` replaces control characters with U+FFFD before\nthey enter the character grid, so one cell stays one column rather than the\ngrid shifting, and the ANSI writer emits the grid unchanged.\n\nPatched in 0.5.0, with backports to 0.4.1 for the 0.4.x line and 0.3.1 for\nusers still on 0.1.5 through 0.3.0. No versions were yanked.\n\nUsers who cannot upgrade should sanitize labels before passing them to\n`kuva`, and avoid interactive SVG output for untrusted data.\n\nNote that `Scene::defs`, `Scene::scripts`, and `Primitive::GroupStart.extra_attrs`\nremain raw escape hatches by design. They are documented as trusted-only and\nare not covered by this advisory; populating them from user data is\nequivalent to executing that data.","state":0,"publishedAt":"2026-08-07T12:00:00+00:00","modifiedAt":"2026-10-13T14:00:03.5413719+00:00","affected":[{"ecosystem":"crates.io","name":"kuva","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"0.1.5"},{"kind":"fixed","version":"0.3.1"},{"kind":"introduced","version":"0.4.0"},{"kind":"fixed","version":"0.4.1"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[],"references":["https://crates.io/crates/kuva","https://rustsec.org/advisories/RUSTSEC-2026-0345.html","https://github.com/Psy-Fer/kuva/commit/2b53ceb796bbb0e9bb537836ad19faac53a51852","https://github.com/Psy-Fer/kuva/commit/2219f5931913f79f3b9be47690b8999a3d8b8bfb","https://github.com/Psy-Fer/kuva/commit/76049ff5133dfb796ba759c2c5e486779d3d30c5","https://github.com/Psy-Fer/kuva/releases/tag/v0.4.1","https://github.com/Psy-Fer/kuva/releases/tag/v0.3.1"],"sourceUrl":"https://osv.dev/vulnerability/RUSTSEC-2026-0345","license":"OSV · upstream source license applies","key":"osv:RUSTSEC-2026-0345","identifiers":["RUSTSEC-2026-0345","GHSA-3c48-9r95-hqhr"]},"retained":false,"historicalAffected":[]}],"coverage":[{"source":"cisa-kev:global","scope":"Complete CISA KEV catalog; all vendors. Missing entries do not establish absence of exploitation.","generation":"2026-11-10T08:21:42.9806814+00:00","complete":true,"records":1773,"lastAttemptAt":"2026-11-10T08:21:42.9806814+00:00","lastSuccessAt":"2026-11-10T08:21:49.9716687+00:00","error":null,"lastVerifiedAt":"2026-11-10T08:21:49.9722477+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"cve-program","scope":"Official CVE Program baseline and cumulative hourly delta; all vendors, published and rejected records","generation":"cve-8859cf40d4337e24bd2f258cc7778fbad01bb24c79d057aaf34cf1dfc6f8917d-746bb7faca3153abeb3c809473890793eb06eef475a9980d578ad522a2f91b4d","complete":true,"records":421910,"lastAttemptAt":"2026-11-10T08:21:35.2839989+00:00","lastSuccessAt":"2026-11-10T08:02:16.532381+00:00","error":null,"lastVerifiedAt":"2026-11-10T08:21:35.2839989+00:00","snapshotPublishedAt":"2026-11-10T07:28:26+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"euvd","scope":"Rolling EUVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global EUVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-2e6a476534094233b48f2f0b6da5cf35","complete":true,"records":13689,"lastAttemptAt":"2026-11-10T08:22:41.0210911+00:00","lastSuccessAt":"2026-11-10T06:05:35.03+00:00","error":null,"lastVerifiedAt":"2026-11-10T06:05:35.03+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13742,"expectedIdentifiers":13742,"failedIdentifiers":0},{"source":"first-epss:nuget","scope":"FIRST EPSS for explicit CVE aliases in the collected public NuGet archive. Not a global EPSS backfill.","generation":"2026-11-10T08:21:42.9806814+00:00","complete":true,"records":1096,"lastAttemptAt":"2026-11-10T08:21:42.9806814+00:00","lastSuccessAt":"2026-11-10T08:22:06.2444793+00:00","error":null,"lastVerifiedAt":"2026-11-10T08:22:06.2444805+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"nvd","scope":"Rolling NVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global NVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-78b08a98b3ce49f188246fb2aae3c44d","complete":false,"records":13695,"lastAttemptAt":"2026-11-10T08:22:40.320966+00:00","lastSuccessAt":"2026-11-10T08:22:40.318+00:00","error":"100 identifier lookups failed; latest failures include request timeout. Other identifiers were checked independently.","lastVerifiedAt":"2026-11-10T08:22:40.318+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13642,"expectedIdentifiers":13742,"failedIdentifiers":100},{"source":"osv:Go","scope":"Complete OSV Go ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"go-cdffb4589856378aa42e8d1bd4a6c74e27a180d8160bf764c44715ef760f0a62","complete":true,"records":9932,"lastAttemptAt":"2026-11-10T08:21:42.331385+00:00","lastSuccessAt":"2026-11-10T00:04:19.8260565+00:00","error":null,"lastVerifiedAt":"2026-11-10T08:21:42.331385+00:00","snapshotPublishedAt":"2026-11-09T23:48:01+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:NuGet","scope":"Complete OSV NuGet ecosystem archive, including GitHub Advisory Database records; NuGet version matching only","generation":"nuget-26503967ae4379db8f374089589d6e03fb15bd31f820c9e992aaf93840c2236c","complete":true,"records":1971,"lastAttemptAt":"2026-11-10T08:21:38.6986948+00:00","lastSuccessAt":"2026-11-09T21:02:20.472473+00:00","error":null,"lastVerifiedAt":"2026-11-10T08:21:38.6986948+00:00","snapshotPublishedAt":"2026-11-09T20:18:18+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:crates.io","scope":"Complete OSV crates.io ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"crates.io-94169eec81ea3989aa551092397ff34ae53c104b4a6ce646e1524452e4ab2a39","complete":true,"records":3065,"lastAttemptAt":"2026-11-10T08:21:42.4406049+00:00","lastSuccessAt":"2026-11-10T08:04:45.5923716+00:00","error":null,"lastVerifiedAt":"2026-11-10T08:21:42.4406049+00:00","snapshotPublishedAt":"2026-11-10T06:48:01+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:npm","scope":"Complete OSV npm ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"npm-4331051b0b2d4ccdbdca3aa196b0be3818a465064a4b18dde4cf39c2250eb490","complete":true,"records":231599,"lastAttemptAt":"2026-11-10T08:21:40.047287+00:00","lastSuccessAt":"2026-11-10T08:04:37.1757322+00:00","error":null,"lastVerifiedAt":"2026-11-10T08:21:40.047287+00:00","snapshotPublishedAt":"2026-11-10T07:33:02+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null}]}