GHSA-47C3-HPMG-7J6P
Vulnerability from github – Published: 2026-10-09 20:56 – Updated: 2026-10-09 20:56Impact
Shiny for Python's bookmark-restore path accepted a client-supplied _state_id_
query-string value and joined it into the server-side bookmark directory
(<cwd>/shiny_bookmarks/<id>) without validating it. A value containing ..
path segments, or an absolute path, could therefore cause the server to open and
parse input.json and values.json from a directory outside the bookmark store.
On a default application the restore was attempted whenever the client supplied a
URL query string — including when bookmarking was disabled
(bookmark_store="disable") — so no opt-in configuration was required. For such
applications the impact is an unauthenticated, server-side probe for the
existence and JSON-validity of attacker-chosen paths, constrained to those two
file names.
Applications configured with bookmark_store="server" and using
ui.input_file() are affected more seriously. The file-input restore handler
copies files out of the restore directory, so the contents of an
attacker-selected file name within an attacker-selected directory could be read
into the application.
Affected versions
1.4.0 through 1.6.3. Bookmarking was introduced in 1.4.0; earlier releases do not contain the affected code path and are not affected.
Patches
Fixed in 1.6.4. Upgrade with:
pip install --upgrade shiny
The fix validates bookmark IDs against an allowlist — a single path segment
matching [A-Za-z0-9_-]+, which cannot express a path separator, a .. parent
reference, or an absolute path — before the ID is used to build a filesystem
path. Bookmark IDs generated by Shiny are unaffected by this restriction.
Restore is additionally gated on the application's bookmark_store setting at a
single point, so it no longer runs when bookmarking is disabled, and the on-disk
restore path is honored only under bookmark_store="server".
Workarounds
There is no configuration change that mitigates this on an affected version:
the restore path ran even with bookmark_store="disable". Deployments that
cannot upgrade immediately can strip the _state_id_ parameter from incoming
query strings at a reverse proxy or load balancer, which prevents the affected
code path from being reached.
Credits
Reported by @0xRenSec.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 1.6.3"
},
"package": {
"ecosystem": "PyPI",
"name": "shiny"
},
"ranges": [
{
"events": [
{
"introduced": "1.4.0"
},
{
"fixed": "1.6.4"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-108258"
],
"database_specific": {
"cwe_ids": [
"CWE-22"
],
"github_reviewed": true,
"github_reviewed_at": "2026-10-09T20:56:37Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "### Impact\n\nShiny for Python\u0027s 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 \u2014 including when bookmarking was disabled\n(`bookmark_store=\"disable\"`) \u2014 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 \u2014 a single path segment\nmatching `[A-Za-z0-9_-]+`, which cannot express a path separator, a `..` parent\nreference, or an absolute path \u2014 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\u0027s `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).",
"id": "GHSA-47c3-hpmg-7j6p",
"modified": "2026-10-09T20:56:37Z",
"published": "2026-10-09T20:56:37Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/posit-dev/py-shiny/security/advisories/GHSA-47c3-hpmg-7j6p"
},
{
"type": "WEB",
"url": "https://github.com/posit-dev/py-shiny/commit/1d8ecb46cbc9621b7dc8812111d26692e086b376"
},
{
"type": "PACKAGE",
"url": "https://github.com/posit-dev/py-shiny"
},
{
"type": "WEB",
"url": "https://github.com/posit-dev/py-shiny/releases/tag/v1.6.4"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "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",
"type": "CVSS_V4"
}
],
"summary": "Shiny for Python has path traversal in bookmark restore"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Browse all ATT&CK techniques and the vulnerabilities related to each.
Related by attack behaviour
Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.