This CVE exposes a path traversal vulnerability in openhole, a tunneling tool that forwards external HTTP requests to locally hosted services. The root cause is deceptively simple: the code used Go's r.URL.Path instead of r.URL.EscapedPath() when proxying requests to the backend.
The distinction matters enormously. r.URL.Path returns the URL-decoded path—so %2e becomes . and %2f becomes /. A developer grabbing what looks like the "clean" path is actually stripping the URL-encoding that would normally neutralize traversal sequences. The API naming subtly incentivizes the unsafe choice; Path sounds like the normal, usable form while EscapedPath sounds like the special case.
This matters specifically because tunneling tools occupy a unique risk posture. When you run openhole, ngrok, or similar tools, you're explicitly telling the software "make my internal service reachable from the internet." The attack surface isn't a hidden admin panel—it's the full internet, directed at whatever you've tunneled. A path traversal through the tunnel doesn't just read files on the tunnel host; it pivots to whatever backend service sits behind it. If that backend is a web app with SSRF-prone code or a database client with weak access controls, the tunnel becomes a lateral movement vector into your internal network.
The fix—swapping Path for EscapedPath—is trivial in code complexity, which is exactly the problem. This is the kind of change a developer makes during refactoring without realizing they're closing a security hole. The commit history almost certainly contains no record of a security decision being made. This suggests the real failure isn't individual negligence; it's that Go's net/http package ships two semantically different path representations with no clear guidance hierarchy between them for security-sensitive contexts.
If you maintain any Go proxy, tunnel, or middleware code that forwards incoming HTTP requests to backends, audit for this pattern. The vulnerable code looks like: req.URL.Path = incoming.Path. The secure form is req.URL.Path = incoming.EscapedPath(). More importantly, treat your tunneling tool's codebase as higher-risk than typical utility code—every URL handling decision there directly exposes your internal network to the internet.