{"id":"CVE-2026-108258","aliases":["EUVD-2026-95986","GHSA-47c3-hpmg-7j6p","PYSEC-2026-4300"],"status":"published","title":"Shiny for Python - Path traversal in bookmark restore","description":"Shiny for Python is a framework for building interactive web applications in Python. From 1.4.0 until 1.6.4, bookmark restore accepts a client-supplied state_id and joins it into the server-side shiny_bookmarks directory without validating that it is a single safe path segment. An unauthenticated request can use parent-directory segments or an absolute path to make the server open input.json and values.json outside the bookmark store, even when bookmark_store is set to disable. In applications configured with bookmark_store set to server and using ui.input_file(), the restore handler can additionally copy and expose an attacker-selected file from an attacker-selected directory. This issue is fixed in version 1.6.4.","severity":{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-11","score":0.00358,"percentile":0.29913,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-22"],"published":"2026-10-09T20:34:39Z","modified":"2026-10-28T02:31:01Z","fixes":[{"ecosystem":"PyPI","package":"shiny","fixed_in":["1.6.4"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"PyPI","package":"shiny","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.4.0"},{"last_affected":"1.6.3"},{"fixed":"1.6.4"}]}]},{"source":"osv","ecosystem":"PyPI","package":"shiny","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.4.0"},{"fixed":"1.6.4"}]}],"versions":["1.4.0","1.5.0","1.5.1","1.6.0","1.6.1","1.6.2","1.6.3"]},{"source":"osv","ecosystem":"PyPI","package":"shiny","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.4.0"},{"fixed":"1.6.4"}]}],"versions":["1.4.0","1.5.0","1.5.1","1.6.0","1.6.1","1.6.2","1.6.3"]}],"products":[{"vendor":"posit-dev","product":"py-shiny"}],"references":[{"url":"https://github.com/posit-dev/py-shiny/security/advisories/GHSA-47c3-hpmg-7j6p","tags":["x_refsource_CONFIRM","advisory","web"]},{"url":"https://github.com/posit-dev/py-shiny/commit/1d8ecb46cbc9621b7dc8812111d26692e086b376","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/posit-dev/py-shiny/releases/tag/v1.6.4","tags":["x_refsource_MISC","web"]},{"url":"https://github.com/advisories/GHSA-47c3-hpmg-7j6p","tags":["advisory"]},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/108xxx/CVE-2026-108258.json","tags":["advisory"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-108258","tags":["advisory"]},{"url":"https://github.com/posit-dev/py-shiny","tags":["package"]},{"url":"https://pypi.org/project/shiny","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-108258","title":"Shiny for Python - Path traversal in bookmark restore","description":"Shiny for Python is a framework for building interactive web applications in Python. From 1.4.0 until 1.6.4, bookmark restore accepts a client-supplied state_id and joins it into the server-side shiny_bookmarks directory without validating that it is a single safe path segment. An unauthenticated request can use parent-directory segments or an absolute path to make the server open input.json and values.json outside the bookmark store, even when bookmark_store is set to disable. In applications configured with bookmark_store set to server and using ui.input_file(), the restore handler can additionally copy and expose an attacker-selected file from an attacker-selected directory. This issue is fixed in version 1.6.4.","status":"published","published":"2026-10-09T20:34:39.539Z","modified":"2026-10-13T18:33:20.463Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N","level":"medium","assessor":"cna"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T14:20:10.510075Z"}],"euvd":[{"id":"EUVD-2026-95986","description":"Shiny for Python is a framework for building interactive web applications in Python. From 1.4.0 until 1.6.4, bookmark restore accepts a client-supplied state_id and joins it into the server-side shiny_bookmarks directory without validating that it is a single safe path segment. An unauthenticated request can use parent-directory segments or an absolute path to make the server open input.json and values.json outside the bookmark store, even when bookmark_store is set to disable. In applications configured with bookmark_store set to server and using ui.input_file(), the restore handler can additionally copy and expose an attacker-selected file from an attacker-selected directory. This issue is fixed in version 1.6.4.","status":"published","published":"2026-10-09T20:34:39Z","modified":"2026-10-13T18:33:20Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N","level":"medium"}],"updated_at":"2026-11-02T17:27:19.128463Z"}],"ghsa":[{"id":"GHSA-47c3-hpmg-7j6p","title":"Shiny for Python has path traversal in bookmark restore","description":"### Impact\n\nShiny for Python's bookmark-restore path accepted a client-supplied `_state_id_`\nquery-string value and joined it into the server-side bookmark directory\n(`\u003ccwd\u003e/shiny_bookmarks/\u003cid\u003e`) without validating it. A value containing `..`\npath segments, or an absolute path, could therefore cause the server to open and\nparse `input.json` and `values.json` from a directory outside the bookmark store.\n\nOn a default application the restore was attempted whenever the client supplied a\nURL query string — including when bookmarking was disabled\n(`bookmark_store=\"disable\"`) — so no opt-in configuration was required. For such\napplications the impact is an unauthenticated, server-side probe for the\nexistence and JSON-validity of attacker-chosen paths, constrained to those two\nfile names.\n\nApplications configured with `bookmark_store=\"server\"` **and** using\n`ui.input_file()` are affected more seriously. The file-input restore handler\ncopies files out of the restore directory, so the contents of an\nattacker-selected file name within an attacker-selected directory could be read\ninto the application.\n\n### Affected versions\n\n1.4.0 through 1.6.3. Bookmarking was introduced in 1.4.0; earlier releases do\nnot contain the affected code path and are not affected.\n\n### Patches\n\nFixed in **1.6.4**. Upgrade with:\n\n```\npip install --upgrade shiny\n```\n\nThe fix validates bookmark IDs against an allowlist — a single path segment\nmatching `[A-Za-z0-9_-]+`, which cannot express a path separator, a `..` parent\nreference, or an absolute path — before the ID is used to build a filesystem\npath. Bookmark IDs generated by Shiny are unaffected by this restriction.\nRestore is additionally gated on the application's `bookmark_store` setting at a\nsingle point, so it no longer runs when bookmarking is disabled, and the on-disk\nrestore path is honored only under `bookmark_store=\"server\"`.\n\n### Workarounds\n\nThere is no configuration change that mitigates this on an affected version:\nthe restore path ran even with `bookmark_store=\"disable\"`. Deployments that\ncannot upgrade immediately can strip the `_state_id_` parameter from incoming\nquery strings at a reverse proxy or load balancer, which prevents the affected\ncode path from being reached.\n\n### Credits\n\nReported by [@0xRenSec](https://github.com/0xRenSec).","status":"published","published":"2026-10-09T20:56:37Z","modified":"2026-10-09T20:56:38Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N","level":"medium"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T14:41:31.175024Z"}],"nvd":[{"id":"CVE-2026-108258","description":"Shiny for Python is a framework for building interactive web applications in Python. From 1.4.0 until 1.6.4, bookmark restore accepts a client-supplied state_id and joins it into the server-side shiny_bookmarks directory without validating that it is a single safe path segment. An unauthenticated request can use parent-directory segments or an absolute path to make the server open input.json and values.json outside the bookmark store, even when bookmark_store is set to disable. In applications configured with bookmark_store set to server and using ui.input_file(), the restore handler can additionally copy and expose an attacker-selected file from an attacker-selected directory. This issue is fixed in version 1.6.4.","status":"published","published":"2026-10-09T21:17:03.853Z","modified":"2026-10-16T17:25:57.94Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"medium","assessor":"secondary"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T14:12:55.473789Z"}],"osv":[{"id":"CVE-2026-108258","title":"Shiny for Python - Path traversal in bookmark restore","description":"Shiny for Python is a framework for building interactive web applications in Python. From 1.4.0 until 1.6.4, bookmark restore accepts a client-supplied state_id and joins it into the server-side shiny_bookmarks directory without validating that it is a single safe path segment. An unauthenticated request can use parent-directory segments or an absolute path to make the server open input.json and values.json outside the bookmark store, even when bookmark_store is set to disable. In applications configured with bookmark_store set to server and using ui.input_file(), the restore handler can additionally copy and expose an attacker-selected file from an attacker-selected directory. This issue is fixed in version 1.6.4.","status":"published","published":"2026-10-09T20:34:39.539Z","modified":"2026-10-28T02:31:01.135521577Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N","level":"medium"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T15:44:54.933106Z"},{"id":"GHSA-47c3-hpmg-7j6p","title":"Shiny for Python has path traversal in bookmark restore","description":"### Impact\n\nShiny for Python's bookmark-restore path accepted a client-supplied `_state_id_`\nquery-string value and joined it into the server-side bookmark directory\n(`\u003ccwd\u003e/shiny_bookmarks/\u003cid\u003e`) without validating it. A value containing `..`\npath segments, or an absolute path, could therefore cause the server to open and\nparse `input.json` and `values.json` from a directory outside the bookmark store.\n\nOn a default application the restore was attempted whenever the client supplied a\nURL query string — including when bookmarking was disabled\n(`bookmark_store=\"disable\"`) — so no opt-in configuration was required. For such\napplications the impact is an unauthenticated, server-side probe for the\nexistence and JSON-validity of attacker-chosen paths, constrained to those two\nfile names.\n\nApplications configured with `bookmark_store=\"server\"` **and** using\n`ui.input_file()` are affected more seriously. The file-input restore handler\ncopies files out of the restore directory, so the contents of an\nattacker-selected file name within an attacker-selected directory could be read\ninto the application.\n\n### Affected versions\n\n1.4.0 through 1.6.3. Bookmarking was introduced in 1.4.0; earlier releases do\nnot contain the affected code path and are not affected.\n\n### Patches\n\nFixed in **1.6.4**. Upgrade with:\n\n```\npip install --upgrade shiny\n```\n\nThe fix validates bookmark IDs against an allowlist — a single path segment\nmatching `[A-Za-z0-9_-]+`, which cannot express a path separator, a `..` parent\nreference, or an absolute path — before the ID is used to build a filesystem\npath. Bookmark IDs generated by Shiny are unaffected by this restriction.\nRestore is additionally gated on the application's `bookmark_store` setting at a\nsingle point, so it no longer runs when bookmarking is disabled, and the on-disk\nrestore path is honored only under `bookmark_store=\"server\"`.\n\n### Workarounds\n\nThere is no configuration change that mitigates this on an affected version:\nthe restore path ran even with `bookmark_store=\"disable\"`. Deployments that\ncannot upgrade immediately can strip the `_state_id_` parameter from incoming\nquery strings at a reverse proxy or load balancer, which prevents the affected\ncode path from being reached.\n\n### Credits\n\nReported by [@0xRenSec](https://github.com/0xRenSec).","status":"published","published":"2026-10-09T20:56:37Z","modified":"2026-10-15T14:56:16.567885576Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N","level":"medium"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T15:46:36.782754Z"},{"id":"PYSEC-2026-4300","title":"Shiny for Python has path traversal in bookmark restore","description":"### Impact\n\nShiny for Python's bookmark-restore path accepted a client-supplied `_state_id_`\nquery-string value and joined it into the server-side bookmark directory\n(`\u003ccwd\u003e/shiny_bookmarks/\u003cid\u003e`) without validating it. A value containing `..`\npath segments, or an absolute path, could therefore cause the server to open and\nparse `input.json` and `values.json` from a directory outside the bookmark store.\n\nOn a default application the restore was attempted whenever the client supplied a\nURL query string — including when bookmarking was disabled\n(`bookmark_store=\"disable\"`) — so no opt-in configuration was required. For such\napplications the impact is an unauthenticated, server-side probe for the\nexistence and JSON-validity of attacker-chosen paths, constrained to those two\nfile names.\n\nApplications configured with `bookmark_store=\"server\"` **and** using\n`ui.input_file()` are affected more seriously. The file-input restore handler\ncopies files out of the restore directory, so the contents of an\nattacker-selected file name within an attacker-selected directory could be read\ninto the application.\n\n### Affected versions\n\n1.4.0 through 1.6.3. Bookmarking was introduced in 1.4.0; earlier releases do\nnot contain the affected code path and are not affected.\n\n### Patches\n\nFixed in **1.6.4**. Upgrade with:\n\n```\npip install --upgrade shiny\n```\n\nThe fix validates bookmark IDs against an allowlist — a single path segment\nmatching `[A-Za-z0-9_-]+`, which cannot express a path separator, a `..` parent\nreference, or an absolute path — before the ID is used to build a filesystem\npath. Bookmark IDs generated by Shiny are unaffected by this restriction.\nRestore is additionally gated on the application's `bookmark_store` setting at a\nsingle point, so it no longer runs when bookmarking is disabled, and the on-disk\nrestore path is honored only under `bookmark_store=\"server\"`.\n\n### Workarounds\n\nThere is no configuration change that mitigates this on an affected version:\nthe restore path ran even with `bookmark_store=\"disable\"`. Deployments that\ncannot upgrade immediately can strip the `_state_id_` parameter from incoming\nquery strings at a reverse proxy or load balancer, which prevents the affected\ncode path from being reached.\n\n### Credits\n\nReported by [@0xRenSec](https://github.com/0xRenSec).","status":"published","published":"2026-10-15T10:22:55.128915Z","modified":"2026-10-15T14:45:13.471383484Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N","level":"medium"}],"updated_at":"2026-11-02T15:52:57.671918Z"}]}}
