@evomap/evolver 1.89.20 → 1.91.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja-JP.md +1 -1
- package/README.ko-KR.md +1 -1
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/index.js +85 -1
- package/package.json +1 -1
- package/src/adapters/claudeCode.js +3 -3
- package/src/adapters/codex.js +2 -2
- package/src/adapters/kiro.js +2 -2
- package/src/adapters/opencode.js +2 -2
- package/src/evolve/guards.js +1 -1
- package/src/evolve/pipeline/collect.js +1 -1
- package/src/evolve/pipeline/dispatch.js +1 -1
- package/src/evolve/pipeline/enrich.js +1 -1
- package/src/evolve/pipeline/hub.js +1 -1
- package/src/evolve/pipeline/select.js +1 -1
- package/src/evolve/pipeline/signals.js +1 -1
- package/src/evolve/utils.js +1 -1
- package/src/evolve.js +1 -1
- package/src/gep/a2aProtocol.js +1 -1
- package/src/gep/antiAbuseTelemetry.js +1 -1
- package/src/gep/autoDistillConv.js +1 -1
- package/src/gep/autoDistillLlm.js +1 -1
- package/src/gep/candidateEval.js +1 -1
- package/src/gep/candidates.js +1 -1
- package/src/gep/contentHash.js +1 -1
- package/src/gep/conversationDistiller.js +1 -1
- package/src/gep/conversationSniffer.js +1 -1
- package/src/gep/crypto.js +1 -1
- package/src/gep/curriculum.js +1 -1
- package/src/gep/deviceId.js +1 -1
- package/src/gep/envFingerprint.js +1 -1
- package/src/gep/epigenetics.js +1 -1
- package/src/gep/execBridge.js +1 -1
- package/src/gep/explore.js +1 -1
- package/src/gep/hash.js +1 -1
- package/src/gep/hubFetch.js +1 -1
- package/src/gep/hubReview.js +1 -1
- package/src/gep/hubSearch.js +1 -1
- package/src/gep/hubVerify.js +1 -1
- package/src/gep/learningSignals.js +1 -1
- package/src/gep/memoryGraph.js +1 -1
- package/src/gep/memoryGraphAdapter.js +1 -1
- package/src/gep/mutation.js +1 -1
- package/src/gep/narrativeMemory.js +1 -1
- package/src/gep/openPRRegistry.js +1 -1
- package/src/gep/personality.js +1 -1
- package/src/gep/policyCheck.js +1 -1
- package/src/gep/prompt.js +1 -1
- package/src/gep/recallInject.js +1 -1
- package/src/gep/recallVerifier.js +1 -1
- package/src/gep/reflection.js +1 -1
- package/src/gep/savingsCore.js +1 -1
- package/src/gep/selector.js +1 -1
- package/src/gep/selfPR.js +83 -44
- package/src/gep/skillDistiller.js +1 -1
- package/src/gep/solidify.js +1 -1
- package/src/gep/strategy.js +1 -1
- package/src/gep/tokenSavings.js +1 -1
- package/src/gep/trajectoryExport.js +1 -1
- package/src/gep/workspaceKeychain.js +1 -1
- package/src/proxy/extensions/traceControl.js +1 -1
- package/src/proxy/inject.js +1 -1
- package/src/proxy/mailbox/store.js +14 -2
- package/src/proxy/sync/outbound.js +44 -3
- package/src/proxy/trace/extractor.js +1 -1
- package/src/proxy/trace/usage.js +1 -1
- package/src/solo/breaker.js +25 -0
- package/src/solo/gitGuard.js +65 -0
package/src/gep/selfPR.js
CHANGED
|
@@ -9,11 +9,39 @@
|
|
|
9
9
|
const fs = require('fs');
|
|
10
10
|
const path = require('path');
|
|
11
11
|
const crypto = require('crypto');
|
|
12
|
-
const {
|
|
12
|
+
const { execFileSync } = require('child_process');
|
|
13
13
|
// 10 MB — prevents RangeError on large child process output (e.g. git log/diff
|
|
14
14
|
// on large repos). See GHSA reports / issue #451.
|
|
15
15
|
const MAX_EXEC_BUFFER = 10 * 1024 * 1024;
|
|
16
16
|
|
|
17
|
+
// SECURITY (Semgrep #285 detect-child-process): every git/gh invocation here
|
|
18
|
+
// goes through argv-form execFileSync (no shell), NEVER string concatenation.
|
|
19
|
+
// PR titles/branches/file paths can carry mutation rationale that is
|
|
20
|
+
// model-generated or hub-fetched (untrusted), and a `git commit -m "..."`
|
|
21
|
+
// shell string would let `$(...)` / backticks in that text execute. Passing
|
|
22
|
+
// each value as a discrete argv element removes the shell entirely, so no
|
|
23
|
+
// metacharacter can break out. Injectable for tests via __test.setExecFile.
|
|
24
|
+
let _execFileImpl = execFileSync;
|
|
25
|
+
|
|
26
|
+
// Run `git <args...>` with no shell. Returns { ok, out } / { ok:false, err }
|
|
27
|
+
// mirroring runGh so callers stay uniform.
|
|
28
|
+
function runGit(args, opts) {
|
|
29
|
+
const o = opts || {};
|
|
30
|
+
try {
|
|
31
|
+
const out = _execFileImpl('git', args, {
|
|
32
|
+
cwd: o.cwd,
|
|
33
|
+
timeout: o.timeoutMs || 10000,
|
|
34
|
+
encoding: 'utf8',
|
|
35
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
36
|
+
maxBuffer: MAX_EXEC_BUFFER,
|
|
37
|
+
env: o.env || process.env,
|
|
38
|
+
});
|
|
39
|
+
return { ok: true, out: String(out || '').trim() };
|
|
40
|
+
} catch (e) {
|
|
41
|
+
return { ok: false, out: '', err: String(e && e.stderr ? e.stderr : e.message || e).slice(0, 500) };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
17
45
|
const { getEvolutionDir, getRepoRoot, getEvolverInstallRoot } = require('./paths');
|
|
18
46
|
const { fullLeakCheck, redactString } = require('./sanitize');
|
|
19
47
|
const {
|
|
@@ -251,11 +279,14 @@ function buildPRTitle(mutation) {
|
|
|
251
279
|
return '[Auto-Mutation] ' + rationale;
|
|
252
280
|
}
|
|
253
281
|
|
|
282
|
+
// Run `gh <args...>` with no shell. `args` is an ARGV ARRAY, not a string, so
|
|
283
|
+
// repo names / branch / title / file paths are discrete arguments the shell
|
|
284
|
+
// never sees.
|
|
254
285
|
function runGh(args, opts) {
|
|
255
286
|
const timeoutMs = (opts && opts.timeoutMs) || SELF_PR_TIMEOUT_MS;
|
|
256
287
|
const cwd = (opts && opts.cwd) || getRepoRoot();
|
|
257
288
|
try {
|
|
258
|
-
const result =
|
|
289
|
+
const result = _execFileImpl('gh', args, {
|
|
259
290
|
cwd: cwd,
|
|
260
291
|
timeout: timeoutMs,
|
|
261
292
|
encoding: 'utf8',
|
|
@@ -272,21 +303,13 @@ function getGitDiff(changedFiles, repoRoot) {
|
|
|
272
303
|
const parts = [];
|
|
273
304
|
for (const f of changedFiles) {
|
|
274
305
|
const before = parts.length;
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
);
|
|
280
|
-
if (result && result.trim()) parts.push(result.trim());
|
|
281
|
-
} catch (_) {}
|
|
306
|
+
// `f` is a discrete argv element after `--`, so a path with quotes or
|
|
307
|
+
// shell metacharacters cannot break out of the command.
|
|
308
|
+
const head = runGit(['diff', 'HEAD', '--', f], { cwd: repoRoot });
|
|
309
|
+
if (head.ok && head.out) parts.push(head.out);
|
|
282
310
|
if (parts.length === before) {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
'git diff -- "' + f + '"',
|
|
286
|
-
{ cwd: repoRoot, timeout: 10000, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'], maxBuffer: MAX_EXEC_BUFFER }
|
|
287
|
-
);
|
|
288
|
-
if (result && result.trim()) parts.push(result.trim());
|
|
289
|
-
} catch (_) {}
|
|
311
|
+
const noHead = runGit(['diff', '--', f], { cwd: repoRoot });
|
|
312
|
+
if (noHead.ok && noHead.out) parts.push(noHead.out);
|
|
290
313
|
}
|
|
291
314
|
}
|
|
292
315
|
return parts.join('\n');
|
|
@@ -359,7 +382,7 @@ async function maybeCreatePR({ capsule, event, mutation, gene, blastRadius }) {
|
|
|
359
382
|
try {
|
|
360
383
|
console.log('[SelfPR] Creating PR on ' + repo + ' branch ' + branch + '...');
|
|
361
384
|
|
|
362
|
-
const forkCheck = runGh('repo view
|
|
385
|
+
const forkCheck = runGh(['repo', 'view', repo, '--json', 'name'], { timeoutMs: 15000 });
|
|
363
386
|
if (!forkCheck.ok) {
|
|
364
387
|
console.warn('[SelfPR] Cannot access repo ' + repo + ': ' + (forkCheck.err || 'unknown'));
|
|
365
388
|
return { attempted: false, reason: 'repo_access_failed' };
|
|
@@ -372,7 +395,7 @@ async function maybeCreatePR({ capsule, event, mutation, gene, blastRadius }) {
|
|
|
372
395
|
fs.mkdirSync(tmpDir, { recursive: true });
|
|
373
396
|
|
|
374
397
|
const cloneResult = runGh(
|
|
375
|
-
'repo clone
|
|
398
|
+
['repo', 'clone', repo, tmpDir, '--', '--depth', '1'],
|
|
376
399
|
{ timeoutMs: 60000 }
|
|
377
400
|
);
|
|
378
401
|
if (!cloneResult.ok) {
|
|
@@ -380,10 +403,9 @@ async function maybeCreatePR({ capsule, event, mutation, gene, blastRadius }) {
|
|
|
380
403
|
return { attempted: false, reason: 'clone_failed' };
|
|
381
404
|
}
|
|
382
405
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
console.warn('[SelfPR] Branch creation failed: ' + (e.message || e));
|
|
406
|
+
const checkoutResult = runGit(['checkout', '-b', branch], { cwd: tmpDir });
|
|
407
|
+
if (!checkoutResult.ok) {
|
|
408
|
+
console.warn('[SelfPR] Branch creation failed: ' + (checkoutResult.err || 'unknown'));
|
|
387
409
|
return { attempted: false, reason: 'branch_failed' };
|
|
388
410
|
}
|
|
389
411
|
|
|
@@ -397,26 +419,38 @@ async function maybeCreatePR({ capsule, event, mutation, gene, blastRadius }) {
|
|
|
397
419
|
}
|
|
398
420
|
}
|
|
399
421
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
422
|
+
const addResult = runGit(['add', '-A'], { cwd: tmpDir });
|
|
423
|
+
if (!addResult.ok) {
|
|
424
|
+
console.warn('[SelfPR] Commit failed: ' + (addResult.err || 'git add failed'));
|
|
425
|
+
return { attempted: false, reason: 'commit_failed' };
|
|
426
|
+
}
|
|
427
|
+
const statusResult = runGit(['status', '--porcelain'], { cwd: tmpDir });
|
|
428
|
+
if (!statusResult.ok) {
|
|
429
|
+
console.warn('[SelfPR] Commit failed: ' + (statusResult.err || 'git status failed'));
|
|
430
|
+
return { attempted: false, reason: 'commit_failed' };
|
|
431
|
+
}
|
|
432
|
+
if (!statusResult.out) {
|
|
433
|
+
console.log('[SelfPR] No changes to commit in public repo clone.');
|
|
434
|
+
return { attempted: false, reason: 'no_public_diff' };
|
|
435
|
+
}
|
|
436
|
+
// title is passed as a discrete argv element, so its content (mutation
|
|
437
|
+
// rationale, possibly model-generated) is never shell-interpreted; no
|
|
438
|
+
// manual quote-escaping needed and `$(...)`/backticks stay literal.
|
|
439
|
+
const commitResult = runGit(['commit', '-m', title], {
|
|
440
|
+
cwd: tmpDir,
|
|
441
|
+
env: Object.assign({}, process.env, {
|
|
442
|
+
GIT_AUTHOR_NAME: 'evolver-bot', GIT_AUTHOR_EMAIL: 'evolver-bot@evomap.ai',
|
|
443
|
+
GIT_COMMITTER_NAME: 'evolver-bot', GIT_COMMITTER_EMAIL: 'evolver-bot@evomap.ai',
|
|
444
|
+
}),
|
|
445
|
+
});
|
|
446
|
+
if (!commitResult.ok) {
|
|
447
|
+
console.warn('[SelfPR] Commit failed: ' + (commitResult.err || 'git commit failed'));
|
|
413
448
|
return { attempted: false, reason: 'commit_failed' };
|
|
414
449
|
}
|
|
415
450
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
console.warn('[SelfPR] Push failed: ' + (e.message || e));
|
|
451
|
+
const pushResult = runGit(['push', 'origin', branch], { cwd: tmpDir, timeoutMs: 30000 });
|
|
452
|
+
if (!pushResult.ok) {
|
|
453
|
+
console.warn('[SelfPR] Push failed: ' + (pushResult.err || 'git push failed'));
|
|
420
454
|
return { attempted: false, reason: 'push_failed' };
|
|
421
455
|
}
|
|
422
456
|
|
|
@@ -424,11 +458,8 @@ async function maybeCreatePR({ capsule, event, mutation, gene, blastRadius }) {
|
|
|
424
458
|
fs.writeFileSync(bodyFile, body);
|
|
425
459
|
|
|
426
460
|
const prResult = runGh(
|
|
427
|
-
'pr create --repo
|
|
428
|
-
|
|
429
|
-
' --title "' + title.replace(/"/g, '\\"') + '"' +
|
|
430
|
-
' --body-file "' + bodyFile + '"' +
|
|
431
|
-
' --label "auto-mutation"',
|
|
461
|
+
['pr', 'create', '--repo', repo, '--head', branch,
|
|
462
|
+
'--title', title, '--body-file', bodyFile, '--label', 'auto-mutation'],
|
|
432
463
|
{ cwd: tmpDir, timeoutMs: 30000 }
|
|
433
464
|
);
|
|
434
465
|
|
|
@@ -466,4 +497,12 @@ module.exports = {
|
|
|
466
497
|
_loadObfuscatedFromManifest: loadObfuscatedFromManifest,
|
|
467
498
|
_resetObfuscatedCache,
|
|
468
499
|
_setManifestRetryTtlForTests,
|
|
500
|
+
// Argv-form exec surface (Semgrep #285): lets tests capture the exact
|
|
501
|
+
// (file, args) handed to execFileSync and assert no shell string is ever
|
|
502
|
+
// built, so a metacharacter-laden title/branch/path cannot inject.
|
|
503
|
+
__test: {
|
|
504
|
+
setExecFile(fn) { _execFileImpl = fn || execFileSync; },
|
|
505
|
+
runGit,
|
|
506
|
+
runGh,
|
|
507
|
+
},
|
|
469
508
|
};
|