fix(security): command injection in npm postinstall + weak RNG fallback (#837)

- Replace `execSync` with `execFileSync` in npm install script to prevent command injection.
- Pass PowerShell paths via environment variables to avoid shell metacharacter interpolation.
- Add `-NoProfile` and `-NonInteractive` flags to PowerShell extraction for cleaner installs.
- Upgrade `makeAccountId` to prioritize `crypto.getRandomValues` over `Math.random` for suffixes.
- Update internal thread title logic to import `collapse_whitespace` from the correct location.

Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
This commit is contained in:
Jwalin Shah
2026-04-23 22:16:44 -07:00
committed by GitHub
co-authored by Steven Enamakel
parent e4c02ba1a6
commit b40af6c294
3 changed files with 22 additions and 9 deletions
+6
View File
@@ -18,6 +18,12 @@ import { AgentChatPanel } from './Conversations';
function makeAccountId(): string {
const c = globalThis.crypto;
if (c && typeof c.randomUUID === 'function') return c.randomUUID();
if (c && typeof c.getRandomValues === 'function') {
const bytes = new Uint8Array(4);
c.getRandomValues(bytes);
const suffix = Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');
return `acct-${Date.now().toString(36)}-${suffix}`;
}
return `acct-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
}
+13 -6
View File
@@ -10,7 +10,7 @@ const https = require('https');
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const { execSync } = require('child_process');
const { execFileSync } = require('child_process');
const REPO = 'tinyhumansai/openhuman';
const pkg = require('./package.json');
@@ -125,17 +125,24 @@ async function main() {
}
console.log('[openhuman] Checksum verified.');
// Extract
// Extract — use execFileSync (no shell interpolation) so paths with spaces
// or shell metacharacters in `tmpTarball` / `binDir` can't be injected.
if (isWin) {
// PowerShell is available on Windows runners
execSync(
`powershell -Command "Expand-Archive -Path '${tmpTarball}' -DestinationPath '${binDir}' -Force"`,
{ stdio: 'inherit' }
execFileSync(
'powershell',
[
'-NoProfile',
'-NonInteractive',
'-Command',
`Expand-Archive -Path $env:TC_SRC -DestinationPath $env:TC_DEST -Force`,
],
{ stdio: 'inherit', env: { ...process.env, TC_SRC: tmpTarball, TC_DEST: binDir } }
);
const extracted = path.join(binDir, 'openhuman-core.exe');
if (fs.existsSync(extracted)) fs.renameSync(extracted, binDest);
} else {
execSync(`tar -xzf "${tmpTarball}" -C "${binDir}"`, { stdio: 'inherit' });
execFileSync('tar', ['-xzf', tmpTarball, '-C', binDir], { stdio: 'inherit' });
const extracted = path.join(binDir, 'openhuman-core');
if (fs.existsSync(extracted)) {
fs.renameSync(extracted, binDest);
+3 -3
View File
@@ -16,9 +16,9 @@ use crate::openhuman::memory::{
};
use crate::openhuman::providers::{self, ProviderRuntimeOptions};
use crate::openhuman::threads::title::{
build_title_prompt, is_auto_generated_thread_title, sanitize_generated_title,
title_log_fingerprint, THREAD_TITLE_LOG_PREFIX, THREAD_TITLE_MODEL_HINT,
THREAD_TITLE_SYSTEM_PROMPT,
build_title_prompt, collapse_whitespace, is_auto_generated_thread_title,
sanitize_generated_title, title_log_fingerprint, THREAD_TITLE_LOG_PREFIX,
THREAD_TITLE_MODEL_HINT, THREAD_TITLE_SYSTEM_PROMPT,
};
use crate::rpc::RpcOutcome;
use serde::Serialize;