{"id":"CVE-2026-108258","aliases":["GHSA-47c3-hpmg-7j6p","PYSEC-2026-4300"],"url":"https://o3.security/vulnerability/CVE-2026-108258","summary":"Shiny for Python - Path traversal in bookmark restore","details":"### 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(`<cwd>/shiny_bookmarks/<id>`) 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).","published":"2026-10-09T20:34:39.539Z","modified":"2026-10-24T02:30:20.284801735Z","cvss":null,"epss":null,"cisaKev":null,"exploitsKnown":null,"affectedPackages":[{"ecosystem":"PyPI","name":"shiny","fixedVersion":"1.6.4"}],"fix":{"url":"https://github.com/posit-dev/py-shiny/commit/1d8ecb46cbc9621b7dc8812111d26692e086b376","label":"posit-dev/py-shiny@1d8ecb4"},"references":[{"type":"WEB","url":"https://github.com/posit-dev/py-shiny/releases/tag/v1.6.4"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/108xxx/CVE-2026-108258.json"},{"type":"ADVISORY","url":"https://github.com/posit-dev/py-shiny/security/advisories/GHSA-47c3-hpmg-7j6p"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-108258"},{"type":"FIX","url":"https://github.com/posit-dev/py-shiny/commit/1d8ecb46cbc9621b7dc8812111d26692e086b376"},{"type":"PACKAGE","url":"https://github.com/posit-dev/py-shiny"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-10-24T02:30:20.284801735Z"}}