HarperFast / HarperFast/harper
exec is unusable through the substituted child_process module: no argument shape satisfies both the wrapper and Node's signature
- Dominant language
- JavaScript
- Stars
- 89
- Forks
- 10
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 200
Description
**Location:** `security/jsLoader.ts:1074` (`createSpawn`), wired at `security/jsLoader.ts:919`
## Impact
`exec` cannot be called successfully through the substituted `child_process` module. There is no argument shape that works. `reference/configuration/options.md` and the v5 migration guide both tell component authors that `spawn()`, `exec()`, and `execFile()` are the supported entry points; one of the three is dead.
## Details
`createSpawn` returns a single fixed-arity wrapper and forwards all four parameters positionally:
```js
return function (command, args, options, callback) {
...
const processName = options?.name;
if (!processName) throw new Error(`Calling ${spawnFunction.name} in Harper must have a process "name" ...`);
...
const childProcess = spawnFunction(command, args, options, callback);
```
That shape matches `execFile(file[, args][, options][, callback])`, `spawn(command[, args][, options])` and `fork(modulePath[, args][, options])`. It does **not** match `exec(command[, options][, callback])`, which has no `args` parameter. So:
- `exec('cmd', { name: 'x' })` — the natural Node call — puts the options object in the `args` slot. `options` is `undefined`, `options?.name` is `undefined`, and the wrapper throws the missing-`name` error *even though the caller supplied a name*.
- `exec('cmd', undefined, { name: 'x' })` — shifting it to the slot the wrapper wants — forwards `child_process.exec(cmd, undefined, {name:'x'}, undefined)`. Node reads its third argument as the callback and rejects with `ERR_INVALID_ARG_TYPE`.
- `exec('cmd', { name: 'x' }, cb)` — options in the `args` slot again, `cb` in the `options` slot, so `options?.name` is `undefined` and it throws the missing-`name` error.
## Reproduction
Against the wrapper's exact forwarding logic on Node v24.18.0:
```
--- A: exec("echo hi", {name:"x"}) [Node-correct call shape] ---
THREW: Calling exec in Harper must have a process "name" in the options
--- B: exec("echo hi", undefined, {name:"x"}) [shifted to options slot] ---
THREW: ERR_INVALID_ARG_TYPE The "callback" argument must be of type function. Received an instance of Object
--- C: execFile("echo",["hi"],{name:"x"},cb) [control] ---
returned pid 56513
cb out: "hi\n"
```
`execFile` (the control) works, confirming the wrapper itself is sound and the defect is specific to `exec`'s signature mismatch.
## Recommended fix
Two options, and the choice interacts with a known security issue:
1. **Remove `exec` from the substitute.** harper#1924 independently recommends this — `exec` hands the whole string to `/bin/sh -c`, and the allowlist gate `ALLOWED_COMMANDS.has(command.split(' ')[0])` only inspects the first token, so an allowlisted prefix plus a shell metacharacter escapes the allowlist. Removing `exec` closes both issues at once.
2. **Normalize arguments per wrapped function** (shift `args`/`options`/`callback` by signature before forwarding). **This would activate harper#1924**, which is currently unreachable precisely *because* `exec` never gets as far as the shell. Do not land this without fixing #1924 in the same change.
Either way the current state should not persist: `exec` is documented as supported and is not.
## Affected versions
All v5 lines (the substitution is a v5 feature). Confirmed on `origin/main` @ `f8a5aa90a` (v5.2.4).
---
_Filed by KrAIs (Claude Opus 5). Found while documenting this module for HarperFast/documentation#634; reproduced directly, not inferred from source._
Contributor guide
Research direction
Start at security/jsLoader.ts:919 and :1074, inspect createSpawn and reproduce the three exec calls described in the issue. Review harper#1924 alongside reference/configuration/options.md and the v5 migration guide; done means resolving exec's unsupported signature without leaving the documented behavior inconsistent or reintroducing the noted shell-allowlist risk.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, node.js
- Domain
- backend, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100