# http-server — Overnight Audit

> **This is a public sample report.** Forgehaven Labs ran the Overnight Audit
> process against [`http-party/http-server`](https://github.com/http-party/http-server),
> a widely used MIT-licensed OSS project, to show exactly what a paying customer
> receives. Nothing here is private, nothing is filed against the maintainers,
> and every finding is reproducible from the commands in Section 7. `http-server`
> is a mature, well-run project; a real customer report on a young private repo
> is usually longer.

**Prepared for:** Public sample (http-party/http-server maintainers, unsolicited)
**Prepared by:** Forgehaven Labs LLC · stevenagreen.com/overnight-audit
**Date:** 2026-07-01 · **Turnaround:** sample (process normally completes in under 24 h)
**Tier:** Repo Audit ($149)
**Scope:** `github.com/http-party/http-server` @ `0d3b7bb` (default branch, 2026-03-13)

---

## 1. The verdict (read this first)

**Overall grade: B** · A mature, genuinely well-tested static file server (532 assertions pass on a fresh clone). The issues are not crashes or exploits in the happy path; they are two developer-facing footguns in the programmatic API and a supply-chain layer that has drifted out of date. None of them should scare a CLI user; two of them will surprise anyone who embeds `http-server` as a library or serves a directory that contains secrets.

> **If you fix only one thing:** run `npm audit fix` to clear the production dependency CVEs (Section 3, P1-2), then read P0-1 if you ever pass options to `createServer()` from code.

---

## 2. Scorecard

| Area | Grade | One-line summary |
|------|-------|------------------|
| Security | B | No injection or path-traversal in the served path; the risks are dotfile exposure and stale transitive CVEs |
| Correctness & error handling | B- | `showDir`/`autoIndex` mis-handle a real boolean `false` in the library API (proven live) |
| Dependencies & supply chain | C | 4 CVEs in production deps, 51 total incl. dev; two core deps effectively unmaintained |
| Build, test & CI | A- | Fresh clone builds; 532 assertions / 42 suites pass; SECURITY.md + CI present |
| Code health & maintainability | B+ | Small, readable, CRLF-injection guard already in place |
| Docs & onboarding | A | README is thorough; every flag documented |

**Verification statement:** every grade is backed by commands we actually ran on a fresh clone at `0d3b7bb`. Section 7 lists them.

---

## 3. Findings

Severity: **P0** fix before you ship · **P1** fix this month · **P2** fix when nearby · **P3** note.

### P1-1 · `showDir: false` (a real boolean) does not disable directory listing in the library API

- **What we found:** The constructor computes `this.showDir = options.showDir !== 'false'`. That compares against the *string* `'false'`. A caller using the documented programmatic API (`require('http-server').createServer({ showDir: false })`) passes a boolean, which is `!== 'false'`, so `showDir` stays **true** and directory listing stays on. The identical pattern applies to `autoIndex` (`options.autoIndex !== 'false'`).
- **Evidence:** `lib/http-server.js:72` and `lib/http-server.js:75`. Proven live on a fresh clone:
  ```
  new HttpServer({showDir: false}).showDir   => true   (bug)
  new HttpServer({showDir: 'false'}).showDir => false  (only the string works)
  ```
- **Impact if ignored:** An embedder who disables directory listing with the natural boolean silently keeps listing enabled, exposing a directory index they believed was off. The CLI is unaffected (it passes strings), so this hides in library use.
- **Fix:** Normalize once at the boundary: `this.showDir = String(options.showDir) !== 'false';` or explicitly `options.showDir !== false && options.showDir !== 'false'`. Add a unit test that passes a boolean.
- **Effort:** 15 min + a test.

### P1-2 · Dotfiles are directly served even though directory listings hide them

- **What we found:** `--no-dotfiles` / the `showDotfiles` option only filters the directory *listing* (`lib/core/show-dir/index.js:67-68`). A direct request to a dotfile path is still served with `200`. So `.env`, `.git/config`, `.htpasswd` do not appear in the index but are fetchable by anyone who guesses the name.
- **Evidence:** Live on a fresh clone serving a folder containing a `.env`:
  ```
  GET /.env  => 200
  body: SECRET=test-not-real
  ```
  The file was absent from the directory listing but returned in full on direct request.
- **Impact if ignored:** Anyone serving a project directory (extremely common with a zero-config server) exposes `.env`, `.git/`, and editor/credential dotfiles to anyone who requests the path, while the hidden listing gives a false sense of safety.
- **Fix:** Make `showDotfiles: false` (the default) also **deny** direct dotfile requests with `403/404`, not just hide them from the listing. At minimum, document loudly in the README that dotfiles are served by path unless denied at the proxy.
- **Effort:** 1 h + test; or one prominent README warning as a stopgap.

### P2-1 · Production dependencies carry 4 known CVEs; 51 advisories across the full tree

- **What we found:** `npm audit --omit=dev` reports 4 vulnerabilities in shipped dependencies; the full tree (with dev tooling) reports 51 (3 low, 21 moderate, 25 high, 2 critical).
- **Evidence:** `npm audit --package-lock-only --omit=dev` on a fresh clone:
  - `minimatch` (direct dep, `^10.1.1`) — **high**, three ReDoS advisories (GHSA-3ppc-4f35-3m26, GHSA-7r86-cg39-jmmj, GHSA-23c5-xmqv-rm74).
  - `lodash` (transitive) — **high**, code injection + prototype pollution.
  - `qs`, `follow-redirects` (transitive via `http-proxy`) — **moderate**, DoS / header leak.
- **Impact if ignored:** ReDoS on `minimatch` is reachable through glob-style path matching; the others are lower but real for a network-facing tool.
- **Fix:** `npm audit fix` clears all four production issues cleanly (the advisory output confirms non-breaking fixes are available). Bump `minimatch` explicitly.
- **Effort:** 15 min + a test run.

### P2-2 · Two core dependencies are effectively unmaintained

- **What we found:** `union` (`~0.5.0`, resolves to 0.6.0, last published 2024-01) and `http-proxy` (`^1.18.1`, last published 2024-12) are both low-activity/legacy packages that `http-server` depends on for its request pipeline and `--proxy` feature.
- **Evidence:** `npm view union time.modified` → `2024-01-18`; `npm view http-proxy time.modified` → `2024-12-13`. `follow-redirects`/`qs` CVEs (P2-1) flow in through `http-proxy`.
- **Impact if ignored:** Security fixes for these deps may never arrive; the project inherits their CVEs with no upstream to patch.
- **Fix:** Track a migration path (`http-proxy-3` / a maintained fork, or drop `union` for a thin custom pipeline). Not urgent; put it on the roadmap.
- **Effort:** Needs design.

### P3-1 · `main` carries an unreleased version bump

- **What we found:** `package.json` says `14.1.2`, but the latest npm publish is `14.1.1`, and `HEAD` is 71 commits ahead of the `v14.1.1` tag.
- **Evidence:** `npm view http-server version` → `14.1.1`; `git rev-list v14.1.1..HEAD --count` → `71`.
- **Impact if ignored:** None functionally; users on npm are 71 commits behind `main`. Worth a release so the security posture on npm matches the repo.
- **Fix:** Cut a `14.1.2` release once P2-1 lands.
- **Effort:** 15 min.

---

## 5. The fix plan (next 30 days)

**Session 1 — 30 min:** `npm audit fix` (P2-1); bump `minimatch`; re-run tests.
**Session 2 — 1 h:** Fix the `showDir`/`autoIndex` boolean handling (P1-1) with tests; decide dotfile-deny policy (P1-2) and either implement or add the README warning.
**Later / opportunistic:** Plan the `http-proxy`/`union` migration (P2-2); cut a release to align npm with `main` (P3-1).
**Explicitly fine to ignore:** Nothing here blocks day-to-day CLI use.

## 6. Quick wins (under 30 minutes each)

1. `npm audit fix` — clears all four production CVEs, non-breaking.
2. Add a `showDir: false` boolean test — locks the P1-1 fix and documents intent.
3. One README line: "dotfiles are served by path; deny `.env`/`.git` at your proxy" — closes the P1-2 surprise for free until the code fix lands.

---

## 7. What we checked (methodology & reproducibility)

**Fleet + human:** an automated agent fleet ran the passes below on a fresh clone of `0d3b7bb`; a human engineer reviewed and reproduced every finding. Nothing here is an unread scanner dump.

| Pass | Key commands (all run on a fresh clone) |
|------|------------------------------------------|
| Fresh-clone build | `npm install` (48 prod packages, then full tree) |
| Test suite | `npm test` → **532 assertions pass, 42/42 suites, 0 fail** |
| Dependency scan | `npm audit --package-lock-only` and `--omit=dev` |
| Dep maintenance | `npm view <pkg> version time.modified` |
| Library-API review | `node -e` harness constructing `HttpServer` with boolean vs string options |
| Dotfile behavior | live server on a temp dir; `curl /.env` |
| Code review | targeted read of `lib/http-server.js`, `lib/core/index.js`, `lib/core/show-dir/` |

**Limits (honest):** we did not run a full penetration test or load test, and we did not audit the `http-proxy` reverse-proxy path against a live upstream. Findings are from static review, the test suite, dependency data, and the runtime probes shown above.

---

*Forgehaven Labs LLC · Overnight Audit · fixed price, 24-hour turnaround.*
*Want this run against your repo? stevenagreen.com/overnight-audit*
