- Location
- jackal-memory/client.py:387
- Finding
- Node Subprocess Inherits All Environment Secrets and Loads Additional Parent .env Data<![CDATA[
## Vulnerability Details
**File Location**: `jackal-memory/client.py:387-400` and `jackal-memory/client.py:421-438`; additional `.env` loading at `jackal-memory/jackal-client.js:48-56`
**Vulnerability Type**: Excessive secret exposure across the subprocess and dependency trust boundary
**Risk Level**: High
### Vulnerable Code
Upload path:
```python
def _jackal_upload(key: str, data_b64: str) -> str:
"""Upload base64-encoded ciphertext to the user's own Jackal VFS. Returns CID."""
mnemonic = _jackal_mnemonic()
if not mnemonic:
print("[jackal-memory] No Jackal wallet found. Run: python client.py walletgen",
file=sys.stderr)
sys.exit(1)
address = _mnemonic_to_jackal_address(mnemonic)
env = {**os.environ, "JACKAL_MNEMONIC": mnemonic, "JACKAL_ADDRESS": address}
r = subprocess.run(
["node", str(_JACKAL_CLIENT), "upload", key],
input=data_b64, text=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
env=env,
)
```
Download path:
```python
def _jackal_download(key: str) -> str:
"""Download ciphertext from the user's own Jackal VFS. Returns base64 ciphertext."""
mnemonic = _jackal_mnemonic()
if not mnemonic:
print("[jackal-memory] No Jackal wallet found.", file=sys.stderr)
sys.exit(1)
address = _mnemonic_to_jackal_address(mnemonic)
safe_key = re.sub(r'[^a-zA-Z0-9._-]', '_', key)
cid = f"Home/jackal-memory/{safe_key}"
env = {**os.environ, "JACKAL_MNEMONIC": mnemonic, "JACKAL_ADDRESS": address}
r = subprocess.run(
["node", str(_JACKAL_CLIENT), "download", cid],
text=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
env=env,
)
```
The Node helper additionally loads arbitrary variables from a parent `.env` file:
```javascript
const fs = require('fs');
const path = require('path');
const envPath = path.join(__dirname, '..', '.env');
if (fs.existsSync(envPath)) {
for (const
...[truncated 2478 chars]
- Remediation
- <![CDATA[
## Remediation Suggestions
1. Construct an explicit subprocess environment allowlist rather than copying `os.environ`:
```python
env = {
"PATH": os.environ.get("PATH", ""),
"HOME": os.environ.get("HOME", ""),
"NODE_ENV": "production",
"JACKAL_MNEMONIC": mnemonic,
"JACKAL_ADDRESS": address,
}
```
2. Include only additional platform variables demonstrably required by Node or the operating system.
3. Remove the implicit parent `.env` parser from `jackal-client.js`.
4. If configuration-file support is necessary, use a dedicated file containing only documented Jackal variables and require an explicit path from the user.
5. Keep `JACKAL_MEMORY_API_KEY` and `JACKAL_MEMORY_ENCRYPTION_KEY` out of the Node subprocess because the helper does not require them.
6. Consider isolating wallet signing in a minimal local process with no network access other than the specific, validated blockchain endpoints.
7. Document the exact environment variables exposed to each subprocess and add automated tests that reject unexpected inherited secrets.
]]>