CVE-2026-59179
OpenHop: Path Traversal in Flow ID File Operations
OpenHop is a tool for creating interactive data-flow diagrams. Prior to 0.3.7, @openhop/server passes unauthenticated flow route parameters from GET /api/flows/:id and DELETE /api/flows/:id in packages/server/src/routes.ts to FlowStore.filePath() in packages/server/src/store.ts without an identifier allow-list. The router applies decodeURIComponent to the id, and path.join() then normalizes traversal outside the configured flow directory after appending the .yaml extension. An attacker can read accessible YAML files as YAML-parsed JSON or permanently delete accessible .yaml files outside that directory, exposing configuration or secrets and disrupting dependent services. In packages/server/src/index.ts, CORS origin: true permits any browser origin, enabling a malicious webpage to target a loopback-bound instance. Docker deployments default to HOST=0.0.0.0 and permit direct unauthenticated network access. This issue is fixed in repository release 0.3.7.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- Required
What an attacker can affect
- Confidentiality
- Low
- Integrity
- High
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used 8.3
OpenHop: Path Traversal in Flow ID File Operations
OpenHop is a tool for creating interactive data-flow diagrams. Prior to 0.3.7, @openhop/server passes unauthenticated flow route parameters from GET /api/flows/:id and DELETE /api/flows/:id in packages/server/src/routes.ts to FlowStore.filePath() in packages/server/src/store.ts without an identifier allow-list. The router applies decodeURIComponent to the id, and path.join() then normalizes traversal outside the configured flow directory after appending the .yaml extension. An attacker can read accessible YAML files as YAML-parsed JSON or permanently delete accessible .yaml files outside that directory, exposing configuration or secrets and disrupting dependent services. In packages/server/src/index.ts, CORS origin: true permits any browser origin, enabling a malicious webpage to target a loopback-bound instance. Docker deployments default to HOST=0.0.0.0 and permit direct unauthenticated network access. This issue is fixed in repository release 0.3.7.
- Record
CVE-2026-59179- CVSS 3.1
- 8.3 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:H/A:H - Weakness
- CWE-22
- Updated
- 2026-11-02
EUVDEUVD-2026-112080 8.3
OpenHop is a tool for creating interactive data-flow diagrams. Prior to 0.3.7, @openhop/server passes unauthenticated flow route parameters from GET /api/flows/:id and DELETE /api/flows/:id in packages/server/src/routes.ts to FlowStore.filePath() in packages/server/src/store.ts without an identifier allow-list. The router applies decodeURIComponent to the id, and path.join() then normalizes traversal outside the configured flow directory after appending the .yaml extension. An attacker can read accessible YAML files as YAML-parsed JSON or permanently delete accessible .yaml files outside that directory, exposing configuration or secrets and disrupting dependent services. In packages/server/src/index.ts, CORS origin: true permits any browser origin, enabling a malicious webpage to target a loopback-bound instance. Docker deployments default to HOST=0.0.0.0 and permit direct unauthenticated network access. This issue is fixed in repository release 0.3.7.
- Record
EUVD-2026-112080- CVSS 3.1
- 8.3 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:H/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-g72f-jw3w-mgh7 8.3
@openhop/server: Path Traversal in Flow ID File Operations
Path Traversal in Flow ID File Operations
Summary
@openhop/server passes unsanitized HTTP route parameters directly to path.join() when constructing filesystem paths for flow YAML files. An unauthenticated attacker who can reach the server can read arbitrary .yaml files accessible to the OpenHop process outside the configured flow directory, and can delete arbitrary .yaml files at any path reachable by the process. Because CORS is set to origin: true (allow all origins), a victim's browser can be used to exploit the vulnerability against a loopback-bound instance. Docker deployments bind HOST=0.0.0.0 by default, enabling direct remote exploitation. CVSS Base Score: 8.3 (High).
Details
FlowStore.filePath() in packages/server/src/store.ts:52–53 constructs a filesystem path by concatenating the caller-supplied id directly into path.join:
// packages/server/src/store.ts:52-53
private filePath(id: string): string {
return join(this.dir, `${id}.yaml`)
}
This result is consumed by two sinks:
- Read (
packages/server/src/store.ts:78):readFile(this.filePath(id), 'utf-8') - Delete (
packages/server/src/store.ts:105):unlink(this.filePath(id))
The id value originates from unauthenticated Fastify HTTP route parameters:
GET /api/flows/:id(packages/server/src/routes.ts:306) →store.get(id)at line 333–335DELETE /api/flows/:id(packages/server/src/routes.ts:509) →store.delete(id)at line 539–541
The route parameter schema at packages/server/src/routes.ts:315 and 519 declares only type: 'string' with no pattern constraint or allowlist. Fastify's underlying router (find-my-way) applies decodeURIComponent to route parameters, so the URL segment ..%2Fvictim is decoded to ../victim before it reaches application code. Node.js path.join('/data/flows', '../victim.yaml') then normalizes to /data/victim.yaml, escaping the configured data directory.
Additionally, packages/server/src/index.ts:37 registers CORS with origin: true, permitting any browser origin to make cross-origin requests to the server. This makes the vulnerability exploitable via a malicious webpage against users running OpenHop locally.
Full data-flow (read path):
- HTTP
GET /api/flows/..%2Fvictimreceived (routes.ts:306) find-my-waydecodes..%2Fvictim→req.params.id = '../victim'(routes.ts:333)store.get('../victim')→filePath('../victim')→join('/data/flows', '../victim.yaml')→/data/victim.yaml(store.ts:52–53)readFile('/data/victim.yaml', 'utf-8')returns file contents (store.ts:78)- Server responds HTTP 200 with YAML-parsed JSON body
Full data-flow (delete path):
- HTTP
DELETE /api/flows/..%2Fdelete-mereceived (routes.ts:509) find-my-waydecodes..%2Fdelete-me→req.params.id = '../delete-me'(routes.ts:539)store.delete('../delete-me')→filePath('../delete-me')→join('/data/flows', '../delete-me.yaml')→/data/delete-me.yaml(store.ts:52–53)unlink('/data/delete-me.yaml')removes the file (store.ts:105)- Server responds HTTP 204
PoC
Environment setup (Docker):
# Build from repository root
docker build -f vuln-001/Dockerfile -t openhop-vuln-001 .
# Run with HOST=0.0.0.0 (default in the Dockerfile ENV)
docker run -d --name openhop-vuln-001 -p 8799:8799 openhop-vuln-001
The container creates /data/flows/ as the configured flow store (OPENHOP_DATA_DIR=/data/flows) and places /data/victim.yaml and /data/delete-me.yaml outside that directory as traversal targets.
Attack 1 — Read file outside flow store:
curl -i --path-as-is 'http://127.0.0.1:8799/api/flows/..%2Fvictim'
Expected response:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{"id":"victim","meta":{"title":"SECRET_OUTSIDE_FILE","description":"This file lives outside the configured flow store…
- Record
GHSA-g72f-jw3w-mgh7- CVSS 3.1
- 8.3 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:H/A:H - Weakness
- CWE-22
- Updated
- 2026-11-02
NVD 8.3
OpenHop is a tool for creating interactive data-flow diagrams. Prior to 0.3.7, @openhop/server passes unauthenticated flow route parameters from GET /api/flows/:id and DELETE /api/flows/:id in packages/server/src/routes.ts to FlowStore.filePath() in packages/server/src/store.ts without an identifier allow-list. The router applies decodeURIComponent to the id, and path.join() then normalizes traversal outside the configured flow directory after appending the .yaml extension. An attacker can read accessible YAML files as YAML-parsed JSON or permanently delete accessible .yaml files outside that directory, exposing configuration or secrets and disrupting dependent services. In packages/server/src/index.ts, CORS origin: true permits any browser origin, enabling a malicious webpage to target a loopback-bound instance. Docker deployments default to HOST=0.0.0.0 and permit direct unauthenticated network access. This issue is fixed in repository release 0.3.7.
- Record
CVE-2026-59179- CVSS 3.1
- 8.3 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:H/A:H - Weakness
- CWE-22
- Updated
- 2026-11-05
OSV 8.3
OpenHop: Path Traversal in Flow ID File Operations
OpenHop is a tool for creating interactive data-flow diagrams. Prior to 0.3.7, @openhop/server passes unauthenticated flow route parameters from GET /api/flows/:id and DELETE /api/flows/:id in packages/server/src/routes.ts to FlowStore.filePath() in packages/server/src/store.ts without an identifier allow-list. The router applies decodeURIComponent to the id, and path.join() then normalizes traversal outside the configured flow directory after appending the .yaml extension. An attacker can read accessible YAML files as YAML-parsed JSON or permanently delete accessible .yaml files outside that directory, exposing configuration or secrets and disrupting dependent services. In packages/server/src/index.ts, CORS origin: true permits any browser origin, enabling a malicious webpage to target a loopback-bound instance. Docker deployments default to HOST=0.0.0.0 and permit direct unauthenticated network access. This issue is fixed in repository release 0.3.7.
- Record
CVE-2026-59179- CVSS 3.1
- 8.3 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:H/A:H - Weakness
- CWE-22
- Updated
- 2026-11-05
OSVGHSA-g72f-jw3w-mgh7 8.3
@openhop/server: Path Traversal in Flow ID File Operations
Path Traversal in Flow ID File Operations
Summary
@openhop/server passes unsanitized HTTP route parameters directly to path.join() when constructing filesystem paths for flow YAML files. An unauthenticated attacker who can reach the server can read arbitrary .yaml files accessible to the OpenHop process outside the configured flow directory, and can delete arbitrary .yaml files at any path reachable by the process. Because CORS is set to origin: true (allow all origins), a victim's browser can be used to exploit the vulnerability against a loopback-bound instance. Docker deployments bind HOST=0.0.0.0 by default, enabling direct remote exploitation. CVSS Base Score: 8.3 (High).
Details
FlowStore.filePath() in packages/server/src/store.ts:52–53 constructs a filesystem path by concatenating the caller-supplied id directly into path.join:
// packages/server/src/store.ts:52-53
private filePath(id: string): string {
return join(this.dir, `${id}.yaml`)
}
This result is consumed by two sinks:
- Read (
packages/server/src/store.ts:78):readFile(this.filePath(id), 'utf-8') - Delete (
packages/server/src/store.ts:105):unlink(this.filePath(id))
The id value originates from unauthenticated Fastify HTTP route parameters:
GET /api/flows/:id(packages/server/src/routes.ts:306) →store.get(id)at line 333–335DELETE /api/flows/:id(packages/server/src/routes.ts:509) →store.delete(id)at line 539–541
The route parameter schema at packages/server/src/routes.ts:315 and 519 declares only type: 'string' with no pattern constraint or allowlist. Fastify's underlying router (find-my-way) applies decodeURIComponent to route parameters, so the URL segment ..%2Fvictim is decoded to ../victim before it reaches application code. Node.js path.join('/data/flows', '../victim.yaml') then normalizes to /data/victim.yaml, escaping the configured data directory.
Additionally, packages/server/src/index.ts:37 registers CORS with origin: true, permitting any browser origin to make cross-origin requests to the server. This makes the vulnerability exploitable via a malicious webpage against users running OpenHop locally.
Full data-flow (read path):
- HTTP
GET /api/flows/..%2Fvictimreceived (routes.ts:306) find-my-waydecodes..%2Fvictim→req.params.id = '../victim'(routes.ts:333)store.get('../victim')→filePath('../victim')→join('/data/flows', '../victim.yaml')→/data/victim.yaml(store.ts:52–53)readFile('/data/victim.yaml', 'utf-8')returns file contents (store.ts:78)- Server responds HTTP 200 with YAML-parsed JSON body
Full data-flow (delete path):
- HTTP
DELETE /api/flows/..%2Fdelete-mereceived (routes.ts:509) find-my-waydecodes..%2Fdelete-me→req.params.id = '../delete-me'(routes.ts:539)store.delete('../delete-me')→filePath('../delete-me')→join('/data/flows', '../delete-me.yaml')→/data/delete-me.yaml(store.ts:52–53)unlink('/data/delete-me.yaml')removes the file (store.ts:105)- Server responds HTTP 204
PoC
Environment setup (Docker):
# Build from repository root
docker build -f vuln-001/Dockerfile -t openhop-vuln-001 .
# Run with HOST=0.0.0.0 (default in the Dockerfile ENV)
docker run -d --name openhop-vuln-001 -p 8799:8799 openhop-vuln-001
The container creates /data/flows/ as the configured flow store (OPENHOP_DATA_DIR=/data/flows) and places /data/victim.yaml and /data/delete-me.yaml outside that directory as traversal targets.
Attack 1 — Read file outside flow store:
curl -i --path-as-is 'http://127.0.0.1:8799/api/flows/..%2Fvictim'
Expected response:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{"id":"victim","meta":{"title":"SECRET_OUTSIDE_FILE","description":"This file lives outside the configured flow store…
- Record
GHSA-g72f-jw3w-mgh7- CVSS 3.1
- 8.3 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:H/A:H - Weakness
- CWE-22
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
@openhop/servernpm | through 0.3.5 | 0.3.6 |
@openhop/servernpm | before 0.3.6 | 0.3.6 |
History
| Recorded | Changed | Sources |
|---|---|---|
| facts | NVD | |
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-59179 is a high vulnerability (CVSS 8.3) in Naorsabag Openhop, published September 9, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.39%, the 33rd percentile. Fixed in @openhop/server 0.3.6.
References
github.com/naorsabag/openhop/security/advisories/GHSA-g72f-jw3w-mgh7
github.com/naorsabag/openhop/pull/190
github.com/naorsabag/openhop/commit/c8190fbefa3a50e7b0c16c001d2e05b0e920cfb4
github.com/naorsabag/openhop/releases/tag/v0.3.7
github.com/advisories/GHSA-g72f-jw3w-mgh7
github.com/CVEProject/cvelistV5/tree/main/cves/2026/59xxx/CVE-2026-59179.json
nvd.nist.gov/vuln/detail/CVE-2026-59179
github.com/naorsabag/openhop
Left out here: the rest of 2 source descriptions. The JSON record has them.