# GHSA-3p34-w4f6-5xh2: better-helperjs Vulnerable to Directory Traversal via String Prefix Bypass in Static Server

> Attackers can read arbitrary files from adjacent directories via a string prefix bypass in the static file server when running in production mode.

- **Severity:** Not scored
- **Exploited in the wild:** Not known
- **Login needed:** Not needed
- **Attackable:** Over the internet
- **Victim action needed:** Not needed
- **Fix available:** Yes
- **Published:** 26 Jun 2026

## What is GHSA-3p34-w4f6-5xh2?

GHSA-3p34-w4f6-5xh2 is a path traversal vulnerability in better-helperjs, published on 26 Jun 2026. An attacker can exploit it over the internet without logging in, with no action from the victim.

### Official description

## Summary
A directory traversal vulnerability exists in the production static file server of `better-helperjs` (`<= 3.0.5`). Attackers can read arbitrary files located in adjacent directory structures that share the same string prefix as the intended static root directory.

## Details
The framework utilizes a custom static file server engine used when running in `NODE_ENV=production` (`src/ssr/site-server.ts`). Inside the `safeStaticPath()` method, the requested path is checked against the static root directory to prevent directory traversal out of the designated public folder.

However, the validation uses `String.prototype.startsWith()`:
```typescript
  const root = path.resolve(rootDir);
  const resolved = path.resolve(root, `.${decodedPath}`);
  
  if (!resolved.startsWith(root)) {
    return null;
  }
```
This is logically flawed because `startsWith` evaluates plain strings rather than structural directory paths. If the application's assigned `rootDir` is `/app/dist/client`, and an attacker tries to access `/app/dist/client-secrets/database.sqlite`, the string `"/app/dist/client-secrets/database.sqlite"` successfully **starts with** `"/app/dist/client"`. 

Because of this bypass, an attacker can read sensitive files stored inside any adjacent directory traversing through the parent, as long as the adjacent directory name begins with the exact same prefix as the target public directory.

## Impact
- **Severity:** High (7.5)
- **Vector:** `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N`
- **Affected Versions:** `<= 3.0.5`
- **Patched Version:** `>= 3.0.6`

## Proof of Concept
To reproduce this locally on a vulnerable installation:
1. Assume the framework static configuration serves from a `.dist/client` directory.
2. Build the project and create an adjacent prefix secret simulating sensitive deployment structure:
   ```bash
   npm run build
   mkdir -p dist/client-secrets
   echo "EXPOSED_SECRET" > dist/client-secrets/secret.txt
   ```
3. Start the application in Production mode (`NODE_ENV=production tsx server.ts`).
4. Using an HTTP client that doesn't pre-normalize `/../` paths (e.g., `netcat` or raw sockets), send a GET request spanning into the prefix-sharing adjacent folder:
   ```http
   GET /%2e%2e%2fclient-secrets/secret.txt HTTP/1.1
   Host: localhost:4174
   Connection: close
   ```
5. The server incorrectly validates the path and responds with `HTTP/1.1 200 OK` exposing `EXPOSED_SECRET`.

*(Note: 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).*

## Remediation / Patches
The path validation block has been updated to mandate exact path separation bounds by enforcing `path.sep` onto the evaluated traversal string.
```typescript
  // Enforces separator OR exact root matches preventing prefix extension
  if (!resolved.startsWith(root + path.sep) && resolved !== root) {
    return null;
  }
```

## Workarounds
If upgrading is temporarily impossible, users can safeguard their environment by ensuring no sensitive directories are deployed adjacent to their static build `client` directories sharing the identical word-prefix string.

## Which versions are affected by GHSA-3p34-w4f6-5xh2?

2 open source packages ship an affected version, including better-helperjs.


| Package | Ecosystem | Affected | Fixed in |
| --- | --- | --- | --- |
| `better-helperjs` | npm | All versions | 3.0.6 |
| `better-helperjs` | npm | All versions | 3.0.6 |

## How do I fix GHSA-3p34-w4f6-5xh2?

Upgrade better-helperjs to 3.0.6 or later.

## Is GHSA-3p34-w4f6-5xh2 being exploited?

There is no known exploitation of GHSA-3p34-w4f6-5xh2 in the wild and no public exploit code.

## Fix prompt for AI coding agents

```text
Check this codebase for GHSA-3p34-w4f6-5xh2 and fix it if it is affected.

What it is: GHSA-3p34-w4f6-5xh2 is a path traversal vulnerability in better-helperjs, published on 26 Jun 2026. An attacker can exploit it over the internet without logging in, with no action from the victim.
Exploitation: There is no known exploitation of GHSA-3p34-w4f6-5xh2 in the wild and no public exploit code.

Affected packages and fixed versions:
- better-helperjs (npm): affected all versions; fixed in 3.0.6
- better-helperjs (npm): affected all versions; fixed in 3.0.6

Task:
1. Search the dependency manifests and lockfiles (for example package.json, package-lock.json, requirements.txt, poetry.lock, uv.lock, pom.xml, build.gradle, go.mod, Cargo.lock, Gemfile.lock) and any Dockerfiles for the affected packages, including transitive dependencies.
2. If an affected version is in use, upgrade it to the fixed version for its release line with the project's package manager, so the lockfile is regenerated. Prefer the smallest upgrade that fixes it.
3. If there is no fix, or the upgrade would break the build, do not guess: explain the risk and the safest workaround instead.
4. Run the tests and summarise what changed and why.
5. If nothing in the project is affected, say so and change nothing.

Reference: https://insights.sbomify.com/GHSA-3p34-w4f6-5xh2/ (Markdown: https://insights.sbomify.com/GHSA-3p34-w4f6-5xh2/index.md)
```

## Sources

- OSV: https://osv.dev/vulnerability/GHSA-3p34-w4f6-5xh2

---
From sbomify Insights: https://insights.sbomify.com/GHSA-3p34-w4f6-5xh2/
Summaries are generated and may contain mistakes; the official description above is the record.
