diff --git a/src/adapters/scripts/evolver-session-end.js b/src/adapters/scripts/evolver-session-end.js index 3e069d1f..4b3eaf03 100755 --- a/src/adapters/scripts/evolver-session-end.js +++ b/src/adapters/scripts/evolver-session-end.js @@ -6,7 +6,7 @@ const fs = require('fs'); const path = require('path'); -const { execSync } = require('child_process'); +const { execSync, spawnSync } = require('child_process'); function findEvolverRoot() { const candidates = [ @@ -101,12 +101,16 @@ function recordToHub(outcome) { summary: outcome.summary, sender_id: nodeId || undefined, }); - const curlCmd = `curl -s -m 8 -X POST` - + ` -H "Content-Type: application/json"` - + ` -H "Authorization: Bearer ${apiKey}"` - + ` -d '${payload.replace(/'/g, "'\\''")}'` - + ` "${hubUrl.replace(/\/+$/, '')}/a2a/evolution/record"`; - execSync(curlCmd, { timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'] }); + // Argv-array form avoids shell interpretation of apiKey, payload, or the + // hub URL. Values cannot break out through quoting metacharacters. + const res = spawnSync('curl', [ + '-s', '-m', '8', '-X', 'POST', + '-H', 'Content-Type: application/json', + '-H', `Authorization: Bearer ${apiKey}`, + '-d', payload, + `${hubUrl.replace(/\/+$/, '')}/a2a/evolution/record`, + ], { timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'], shell: false }); + if (res.status !== 0 || res.error) return false; return true; } catch { return false; diff --git a/src/gep/signals.js b/src/gep/signals.js index 30aa79ae..5e9fb587 100644 --- a/src/gep/signals.js +++ b/src/gep/signals.js @@ -254,24 +254,29 @@ function _extractLLM(corpus) { var url = hubUrl + '/a2a/signal/analyze'; - // Use execSync + curl for truly synchronous HTTP. Node's http.request() is - // async and its callbacks cannot fire inside a synchronous spin-wait loop - // because execSync blocks the event loop. - var curlCmd = 'curl -s -m 10 -X POST' - + ' -H "Content-Type: application/json"' - + ' -H "Authorization: Bearer ' + nodeSecret + '"' - + ' -d ' + JSON.stringify(postData).replace(/'/g, "'\\''") - + ' ' + JSON.stringify(url); - - var execSync = require('child_process').execSync; + // Use spawnSync + curl for truly synchronous HTTP. Node's http.request() + // is async and its callbacks cannot fire inside a synchronous spin-wait + // loop because a synchronous subprocess blocks the event loop. Argv-array + // form avoids shell interpretation, so values in nodeSecret, postData, or + // url cannot escape through shell metacharacters. + var spawnSync = require('child_process').spawnSync; var stdout = ''; try { - stdout = execSync(curlCmd, { + var res = spawnSync('curl', [ + '-s', '-m', '10', '-X', 'POST', + '-H', 'Content-Type: application/json', + '-H', 'Authorization: Bearer ' + nodeSecret, + '-d', postData, + url, + ], { timeout: 12000, windowsHide: true, stdio: ['pipe', 'pipe', 'pipe'], encoding: 'utf8', + shell: false, }); + if (res.status !== 0 || res.error) return []; + stdout = res.stdout || ''; } catch (_) { return []; }