HarperFast / HarperFast/harper
[security] checkAllowedModulePath uses prefix match without a path-separator boundary
- Dominant language
- JavaScript
- Stars
- 89
- Forks
- 10
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 200
Description
**Severity:** LOW · **Category:** `path-traversal` · **CWE-22**
**Location:** `security/jsLoader.ts:1091` in `checkAllowedModulePath`
## Impact
A component could read/execute another component's module source that resides in a sibling directory sharing a name prefix, breaking per-component file confinement.
## Details
The module-path confinement uses `path.startsWith(allowedPath)` with no trailing-separator boundary, so a sibling path whose name merely begins with allowedPath (e.g. allowedPath `/opt/harper/app1` vs `/opt/harper/app1-evil`) is treated as inside the allowed directory, letting a component load/execute module source outside its intended directory.
## Exploit scenario
Component confined to /opt/harper/components/app imports a relative/absolute specifier resolving to /opt/harper/components/app-shared/secret.js; realpath starts with the allowed prefix, so the guard passes and the out-of-scope file is loaded.
## Preconditions
- A sibling directory exists whose absolute path starts with the allowed path string
- Component-supplied module specifiers resolve into that sibling directory
## Recommended fix
Normalize allowedPath to end with the platform path separator (or compare with `path === allowedPath || path.startsWith(allowedPath + sep)`) before the prefix comparison.
---
Found by an automated multi-agent security review (Claude Security) against `origin/main` @ `2615b092b`, confirmed by a three-lens verification panel. Line numbers are as of that commit. No code was executed; derived from source review, so validate before remediation.
Contributor guide
Research direction
Start in security/jsLoader.ts at checkAllowedModulePath around line 1091 and inspect how resolved paths are compared with allowedPath. Validate the boundary behavior using the sibling-directory scenario described in the issue. Done means descendants of the allowed directory remain accepted while similarly prefixed sibling paths are rejected.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- nodejs, typescript
- Domain
- backend, security
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 77/100