@tangle-network/agent-bench 0.8.32 → 0.9.1
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/CHANGELOG.md +15 -0
- package/HARNESS.md +11 -2
- package/dist/index.d.ts +27 -10
- package/dist/index.js +135 -38
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/scripts/verify-pier-agent.mts +1 -1
- package/scripts/verify-pier-recovery.mts +2 -1
- package/src/index.ts +1 -0
- package/src/official-optimizer-config.mts +17 -8
- package/src/pier-agent.test.mts +1 -1
- package/src/pier-agent.ts +3 -2
- package/src/pier-trial-controller.test.mts +7 -5
- package/src/pier-trial-controller.ts +2 -1
- package/src/run-benchmarks.test.mts +86 -4
- package/src/run-benchmarks.ts +128 -28
- package/src/sandbox-run.ts +2 -1
- package/src/swe-arena/activation.test.mts +1 -0
- package/src/swe-arena/briefing.test.mts +1 -0
- package/src/swe-arena/execution.test.mts +3 -2
- package/src/swe-arena/factory.test.mts +5 -3
- package/src/swe-arena/gepa-seat.mts +4 -2
- package/src/swe-arena/gepa-seat.test.mts +70 -3
- package/src/swe-arena/premeasured-from-cells.test.mts +1 -0
- package/src/swe-arena/proc.test.mts +3 -5
- package/src/swe-arena/proposer-fanout.test.mts +1 -0
- package/src/swe-arena/run-experiment.mts +2 -0
- package/src/swe-arena/scratch-worktree.test.mts +2 -1
- package/src/swe-bench-env.test.ts +9 -1
|
@@ -152,6 +152,7 @@ describe('briefing text', () => {
|
|
|
152
152
|
const repo = await mkdtemp(join(tmpdir(), 'briefing-repo-'))
|
|
153
153
|
try {
|
|
154
154
|
await runOk('git', ['init', '-q', '-b', 'main', repo])
|
|
155
|
+
await runOk('git', ['-C', repo, 'config', 'core.hooksPath', '/dev/null'])
|
|
155
156
|
await runOk('git', ['-C', repo, 'config', 'user.email', 't@t.dev'])
|
|
156
157
|
await runOk('git', ['-C', repo, 'config', 'user.name', 'T'])
|
|
157
158
|
await writeFile(join(repo, 'base.txt'), 'x\n')
|
|
@@ -344,6 +344,7 @@ describe('arms: patch extraction (real git, no docker)', () => {
|
|
|
344
344
|
const ws = await scratch('swe-arena-git-')
|
|
345
345
|
const git = (...argv: string[]) => runOk('git', ['-C', ws, ...argv])
|
|
346
346
|
await runOk('git', ['init', '-q', ws])
|
|
347
|
+
await runOk('git', ['-C', ws, 'config', 'core.hooksPath', '/dev/null'])
|
|
347
348
|
await git('config', 'user.email', 't@t')
|
|
348
349
|
await git('config', 'user.name', 't')
|
|
349
350
|
await writeFile(join(ws, 'lib.py'), 'x = 1\n')
|
|
@@ -904,7 +905,7 @@ describe('serialized-judge', () => {
|
|
|
904
905
|
const dir = await scratch('swe-arena-judge-flock-stale-bytes-')
|
|
905
906
|
const lockFile = join(dir, 'lock')
|
|
906
907
|
await writeFile(lockFile, 'half-written-or-dead-owner\n')
|
|
907
|
-
await expect(withJudgeLock(lockFile, async () => 'acquired', { timeoutMs:
|
|
908
|
+
await expect(withJudgeLock(lockFile, async () => 'acquired', { timeoutMs: 5_000 })).resolves.toBe('acquired')
|
|
908
909
|
expect(await readFile(lockFile, 'utf8')).toBe('half-written-or-dead-owner\n')
|
|
909
910
|
})
|
|
910
911
|
|
|
@@ -926,7 +927,7 @@ describe('serialized-judge', () => {
|
|
|
926
927
|
|
|
927
928
|
await expect(result).rejects.toThrow('cancelled active judge')
|
|
928
929
|
expect((await readFile(attempts, 'utf8')).trim().split('\n')).toEqual(['attempt'])
|
|
929
|
-
await expect(withJudgeLock(lockFile, async () => 'released', { timeoutMs:
|
|
930
|
+
await expect(withJudgeLock(lockFile, async () => 'released', { timeoutMs: 5_000 })).resolves.toBe('released')
|
|
930
931
|
}, 15_000)
|
|
931
932
|
|
|
932
933
|
it('bounds stale-container cleanup before starting the judge child', async () => {
|
|
@@ -246,6 +246,7 @@ async function makeSyntheticMirror(root: string): Promise<SyntheticMirror> {
|
|
|
246
246
|
const mirror = join(root, 'mirror')
|
|
247
247
|
await mkdir(mirror, { recursive: true })
|
|
248
248
|
await runOk('git', ['-C', mirror, 'init', '-q', '-b', 'main'])
|
|
249
|
+
await runOk('git', ['-C', mirror, 'config', 'core.hooksPath', '/dev/null'])
|
|
249
250
|
await writeFile(join(mirror, 'README.md'), '# synthetic\n')
|
|
250
251
|
await writeFile(
|
|
251
252
|
join(mirror, 'package.json'),
|
|
@@ -359,7 +360,8 @@ describe('parseVitestSummary', () => {
|
|
|
359
360
|
})
|
|
360
361
|
})
|
|
361
362
|
|
|
362
|
-
|
|
363
|
+
// The isolated Docker controller uses the Linux daemon socket, never personal Docker contexts.
|
|
364
|
+
describe.skipIf(process.platform !== 'linux')('factory command credential isolation', () => {
|
|
363
365
|
it('blocks arbitrary env, auth sockets, npm config, and home credentials from a package lifecycle script', async () => {
|
|
364
366
|
const ambientHome = join(root, 'ambient-home')
|
|
365
367
|
const ambientXdg = join(root, 'ambient-xdg')
|
|
@@ -506,7 +508,7 @@ describe('loadFactoryInstance', () => {
|
|
|
506
508
|
// Judge child pipeline on the synthetic fixture.
|
|
507
509
|
// ---------------------------------------------------------------------------
|
|
508
510
|
|
|
509
|
-
describe('judgeFactoryPatch', () => {
|
|
511
|
+
describe.skipIf(process.platform !== 'linux')('judgeFactoryPatch', () => {
|
|
510
512
|
it('gold (impl-only PR diff) resolves with full score', async () => {
|
|
511
513
|
const { result } = await judgeFactoryPatch(goodInst, await goldImplPatch(goodInst))
|
|
512
514
|
expect(result).toMatchObject({ resolved: true, score: 1, passed: 2, total: 2 })
|
|
@@ -597,7 +599,7 @@ describe('materializeFactoryWorkspace', () => {
|
|
|
597
599
|
// Calibration admission gate — both rejection directions.
|
|
598
600
|
// ---------------------------------------------------------------------------
|
|
599
601
|
|
|
600
|
-
describe('calibrateFactoryInstance', () => {
|
|
602
|
+
describe.skipIf(process.platform !== 'linux')('calibrateFactoryInstance', () => {
|
|
601
603
|
it('admits a well-formed instance (gold passes, base fails)', async () => {
|
|
602
604
|
const r = await calibrateFactoryInstance(goodInst)
|
|
603
605
|
expect(r).toMatchObject({
|
|
@@ -130,7 +130,7 @@ export type GepaSeatRecipe = Extract<
|
|
|
130
130
|
/** Build the bounded recipe for a seat. The TOTAL inner-evaluation budget is
|
|
131
131
|
* exactly `maxMetricCalls`. The adapter's local callback enforces the sum
|
|
132
132
|
* of per-run limits, and the seat's own dispatch wrapper re-enforces it. */
|
|
133
|
-
export function recipeForSeat(spec: GepaSeatSpec): GepaSeatRecipe {
|
|
133
|
+
export function recipeForSeat(spec: GepaSeatSpec, optimizerModel?: string): GepaSeatRecipe {
|
|
134
134
|
const calls = spec.maxMetricCalls ?? DEFAULT_MAX_METRIC_CALLS
|
|
135
135
|
const cost = spec.maxProposerCostUsd ?? DEFAULT_MAX_PROPOSER_COST_USD
|
|
136
136
|
if (spec.engine === 'gepa') {
|
|
@@ -145,6 +145,7 @@ export function recipeForSeat(spec: GepaSeatSpec): GepaSeatRecipe {
|
|
|
145
145
|
engine,
|
|
146
146
|
maxEvaluations: perExplore,
|
|
147
147
|
maxProposerCostUsd: perRunCost,
|
|
148
|
+
...(engine !== 'gepa' && optimizerModel ? { engineConfig: { model: optimizerModel } } : {}),
|
|
148
149
|
}))
|
|
149
150
|
return {
|
|
150
151
|
kind: 'omni',
|
|
@@ -758,6 +759,7 @@ export function gepaSeatAuthor(config: OuterLoopConfig, deps: GepaSeatDeps): Aut
|
|
|
758
759
|
: officialOptimizerModel({
|
|
759
760
|
env: process.env,
|
|
760
761
|
envPrefix: 'GEPA_OPTIMIZER',
|
|
762
|
+
anthropicEndpoint: spec.engine === 'omni',
|
|
761
763
|
model: process.env.GEPA_OPTIMIZER_MODEL ?? config.arm.driverModel,
|
|
762
764
|
baseUrl:
|
|
763
765
|
process.env.GEPA_OPTIMIZER_BASE_URL ??
|
|
@@ -771,7 +773,7 @@ export function gepaSeatAuthor(config: OuterLoopConfig, deps: GepaSeatDeps): Aut
|
|
|
771
773
|
}))
|
|
772
774
|
const method = factory({
|
|
773
775
|
name: `gepa-seat:${spec.name}`,
|
|
774
|
-
recipe,
|
|
776
|
+
recipe: recipeForSeat(spec, optimizer?.model),
|
|
775
777
|
objective,
|
|
776
778
|
evaluationId: gepaSeatEvaluationId({
|
|
777
779
|
smokeInstanceId: deps.smokeInstanceId,
|
|
@@ -4,9 +4,12 @@ import { mkdir, mkdtemp, readFile, readdir, rm, writeFile } from 'node:fs/promis
|
|
|
4
4
|
import { createServer } from 'node:http'
|
|
5
5
|
import { tmpdir } from 'node:os'
|
|
6
6
|
import { join } from 'node:path'
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
import {
|
|
8
|
+
gepaOptimizationMethod,
|
|
9
|
+
createRunCostLedger,
|
|
10
|
+
fsCampaignStorage,
|
|
11
|
+
type DispatchContext,
|
|
12
|
+
type OptimizationMethodProvenance,
|
|
10
13
|
} from '@tangle-network/agent-eval/campaign'
|
|
11
14
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
12
15
|
import { ACTIVATION_PREDICATE_RELPATH, parseActivationPredicate } from './activation.mts'
|
|
@@ -89,6 +92,68 @@ describe('validateGepaSeat', () => {
|
|
|
89
92
|
// ---------------------------------------------------------------------------
|
|
90
93
|
|
|
91
94
|
describe('recipeForSeat', () => {
|
|
95
|
+
it.each(['gepa', 'omni'] as const)('transports the real %s optimizer configuration', async (engine) => {
|
|
96
|
+
const runDir = await mkdtemp(join(tmpdir(), 'gepa-seat-transport-'))
|
|
97
|
+
const requests: unknown[] = []
|
|
98
|
+
try {
|
|
99
|
+
const optimizer = officialOptimizerModel({
|
|
100
|
+
env: { OPT_INPUT_USD_PER_MILLION: '1', OPT_CACHED_INPUT_USD_PER_MILLION: '1', OPT_CACHE_WRITE_USD_PER_MILLION: '1', OPT_OUTPUT_USD_PER_MILLION: '1' },
|
|
101
|
+
envPrefix: 'OPT', model: 'fixture-model', baseUrl: 'http://127.0.0.1:1/v1', apiKey: 'fixture-key',
|
|
102
|
+
maxCostUsd: 1, maxOutputTokensPerRequest: 100, anthropicEndpoint: engine === 'omni',
|
|
103
|
+
complete: async (request) => {
|
|
104
|
+
requests.push(request)
|
|
105
|
+
return { model: 'fixture-model', choices: [{ message: { content: 'ok' }, finish_reason: 'stop' }], usage: { prompt_tokens: 2, completion_tokens: 1, cost: 0.000003 } }
|
|
106
|
+
},
|
|
107
|
+
})
|
|
108
|
+
const spec = seat({ engine })
|
|
109
|
+
validateGepaSeat(spec)
|
|
110
|
+
const runtime = {
|
|
111
|
+
python: { implementation: 'CPython', version: '3.12.0' },
|
|
112
|
+
bridge: { package: 'agent-eval-rpc', version: 'fixture', sourceUrl: 'https://github.com/tangle-network/agent-eval', revision: 'fixture', sourceSha256: 'a'.repeat(64) },
|
|
113
|
+
optimizer: { package: 'gepa', version: 'fixture', sourceUrl: 'https://github.com/gepa-ai/gepa', revision: 'fixture', sourceSha256: 'b'.repeat(64) },
|
|
114
|
+
engineModules: [],
|
|
115
|
+
}
|
|
116
|
+
// Only the child optimizer is substituted; the real Eval proxy calls Runtime.
|
|
117
|
+
const bridge = join(runDir, 'transport.mjs')
|
|
118
|
+
await writeFile(bridge, `
|
|
119
|
+
import fs from 'node:fs'
|
|
120
|
+
const input = JSON.parse(fs.readFileSync(process.argv[process.argv.indexOf('--input') + 1], 'utf8'))
|
|
121
|
+
if (input.operation === 'inspect') {
|
|
122
|
+
fs.writeFileSync(process.argv[process.argv.indexOf('--output') + 1], JSON.stringify({ runtime: ${JSON.stringify(runtime)} }))
|
|
123
|
+
} else {
|
|
124
|
+
const omni = input.recipe.kind === 'omni'
|
|
125
|
+
if (omni && input.recipe.explore.some(run => run.engine !== 'gepa' && run.engineConfig.model !== input.modelProxy.model)) throw new Error('wrong engine model')
|
|
126
|
+
const send = maxTokens => fetch(input.modelProxy.baseUrl + (omni ? '/messages' : '/chat/completions'), {
|
|
127
|
+
method: 'POST',
|
|
128
|
+
headers: { 'content-type': 'application/json', authorization: 'Bearer ' + input.modelProxy.apiKey, 'x-api-key': input.modelProxy.apiKey, 'anthropic-version': '2023-06-01' },
|
|
129
|
+
body: JSON.stringify({ model: input.modelProxy.model, max_tokens: maxTokens, messages: [{ role: 'user', content: 'transport proof' }] }),
|
|
130
|
+
})
|
|
131
|
+
const overBudget = await send(101)
|
|
132
|
+
if (overBudget.ok) throw new Error('output ceiling was not enforced')
|
|
133
|
+
await overBudget.text()
|
|
134
|
+
const response = await send(10)
|
|
135
|
+
if (!response.ok) throw new Error('model transport failed: ' + response.status + ' ' + await response.text())
|
|
136
|
+
await response.json()
|
|
137
|
+
throw new Error('transport-handshake-complete')
|
|
138
|
+
}
|
|
139
|
+
`)
|
|
140
|
+
const method = gepaOptimizationMethod({
|
|
141
|
+
recipe: recipeForSeat(spec, optimizer.model), optimizer,
|
|
142
|
+
objective: 'Check transport only', evaluationId: 'bench-transport',
|
|
143
|
+
runner: { command: process.execPath, args: [bridge] },
|
|
144
|
+
})
|
|
145
|
+
await expect(method.optimize({
|
|
146
|
+
baselineSurface: 'seed', trainScenarios: [{ id: 'train', kind: 'fixture' }], selectionScenarios: [{ id: 'selection', kind: 'fixture' }],
|
|
147
|
+
dispatchWithSurface: async () => 'unused',
|
|
148
|
+
judges: [{ name: 'fixture', dimensions: [{ key: 'score', description: 'fixture' }], score: async () => ({ dimensions: { score: 1 }, composite: 1 }) }],
|
|
149
|
+
runDir, seed: 42, runOptions: {}, costLedger: createRunCostLedger({ storage: fsCampaignStorage(), runDir: join(runDir, 'cost') }),
|
|
150
|
+
})).rejects.toThrow('transport-handshake-complete')
|
|
151
|
+
expect(requests).toHaveLength(1)
|
|
152
|
+
} finally {
|
|
153
|
+
await rm(runDir, { recursive: true, force: true })
|
|
154
|
+
}
|
|
155
|
+
})
|
|
156
|
+
|
|
92
157
|
it("'gepa' is one bounded engine run carrying the full budget (default 10)", () => {
|
|
93
158
|
const recipe = recipeForSeat(seat() as GepaSeatSpec)
|
|
94
159
|
expect(recipe).toMatchObject({ kind: 'engine', run: { engine: 'gepa', maxEvaluations: DEFAULT_MAX_METRIC_CALLS } })
|
|
@@ -535,6 +600,7 @@ describe('fanOutLoopsGenerator with the gepa seat', () => {
|
|
|
535
600
|
loopsRepo = await mkdtemp(join(tmpdir(), 'gepa-repo-'))
|
|
536
601
|
outDir = await mkdtemp(join(tmpdir(), 'gepa-out-'))
|
|
537
602
|
await runOk('git', ['init', '-q', '-b', 'main', loopsRepo])
|
|
603
|
+
await runOk('git', ['-C', loopsRepo, 'config', 'core.hooksPath', '/dev/null'])
|
|
538
604
|
await git(['config', 'user.email', 't@t.dev'], loopsRepo)
|
|
539
605
|
await git(['config', 'user.name', 'T'], loopsRepo)
|
|
540
606
|
await mkdir(join(loopsRepo, 'extensions', 'pi', 'prompts'), { recursive: true })
|
|
@@ -995,6 +1061,7 @@ describe('integration: real adapter roundtrip', () => {
|
|
|
995
1061
|
const outDir = await mkdtemp(join(tmpdir(), 'gepa-int-out-'))
|
|
996
1062
|
try {
|
|
997
1063
|
await runOk('git', ['init', '-q', '-b', 'main', loopsRepo])
|
|
1064
|
+
await runOk('git', ['-C', loopsRepo, 'config', 'core.hooksPath', '/dev/null'])
|
|
998
1065
|
await runOk('git', ['-C', loopsRepo, 'config', 'user.email', 't@t.dev'])
|
|
999
1066
|
await runOk('git', ['-C', loopsRepo, 'config', 'user.name', 'T'])
|
|
1000
1067
|
await mkdir(join(loopsRepo, 'extensions', 'pi', 'prompts'), { recursive: true })
|
|
@@ -182,6 +182,7 @@ describe('loadFullCampaignCells + incumbentSurfaceHash (real fs/git)', () => {
|
|
|
182
182
|
|
|
183
183
|
it('computes the same incumbent hash for the unchanged tip (baseCommit == candidateCommit, empty patch)', async () => {
|
|
184
184
|
await runOk('git', ['init', '-q', '-b', 'main', repo])
|
|
185
|
+
await runOk('git', ['-C', repo, 'config', 'core.hooksPath', '/dev/null'])
|
|
185
186
|
await runOk('git', ['-C', repo, 'config', 'user.email', 't@t.dev'])
|
|
186
187
|
await runOk('git', ['-C', repo, 'config', 'user.name', 'T'])
|
|
187
188
|
await writeFile(join(repo, 'a.txt'), 'x\n')
|
|
@@ -65,18 +65,16 @@ describe('run process-group timeout', () => {
|
|
|
65
65
|
const dir = await mkdtemp(join(tmpdir(), 'swe-proc-tree-'))
|
|
66
66
|
dirs.push(dir)
|
|
67
67
|
const pidFile = join(dir, 'grandchild.pid')
|
|
68
|
-
const grandchild = `process.on('SIGTERM',()=>{}); setInterval(()=>{},1000)`
|
|
68
|
+
const grandchild = `process.on('SIGTERM',()=>{}); require('node:fs').writeFileSync(process.argv[1],String(process.pid)); setInterval(()=>{},1000)`
|
|
69
69
|
const parent = [
|
|
70
70
|
`const {spawn}=require('node:child_process')`,
|
|
71
|
-
`
|
|
72
|
-
`const child=spawn(process.execPath,['-e',${JSON.stringify(grandchild)}],{stdio:'ignore'})`,
|
|
73
|
-
`fs.writeFileSync(process.argv[1],String(child.pid))`,
|
|
71
|
+
`spawn(process.execPath,['-e',${JSON.stringify(grandchild)},process.argv[1]],{stdio:'ignore'})`,
|
|
74
72
|
`process.on('SIGTERM',()=>process.exit(0))`,
|
|
75
73
|
`setInterval(()=>{},1000)`,
|
|
76
74
|
].join(';')
|
|
77
75
|
|
|
78
76
|
const result = await run(process.execPath, ['-e', parent, pidFile], {
|
|
79
|
-
timeoutMs:
|
|
77
|
+
timeoutMs: 2_000,
|
|
80
78
|
killGraceMs: 50,
|
|
81
79
|
})
|
|
82
80
|
const grandchildPid = Number(await readFile(pidFile, 'utf8'))
|
|
@@ -358,6 +358,7 @@ describe('fanOutLoopsGenerator', () => {
|
|
|
358
358
|
loopsRepo = await mkdtemp(join(tmpdir(), 'fanout-repo-'))
|
|
359
359
|
outDir = await mkdtemp(join(tmpdir(), 'fanout-out-'))
|
|
360
360
|
await runOk('git', ['init', '-q', '-b', 'main', loopsRepo])
|
|
361
|
+
await runOk('git', ['-C', loopsRepo, 'config', 'core.hooksPath', '/dev/null'])
|
|
361
362
|
await git(['config', 'user.email', 't@t.dev'], loopsRepo)
|
|
362
363
|
await git(['config', 'user.name', 'T'], loopsRepo)
|
|
363
364
|
await writeFile(join(loopsRepo, 'src.ts'), 'base\n')
|
|
@@ -399,6 +399,8 @@ export async function materializeFactoryWorkspace(
|
|
|
399
399
|
await writeFile(join(dest, 'SPEC.md'), inst.spec)
|
|
400
400
|
|
|
401
401
|
await runOk('git', ['-C', dest, 'init', '-q', '-b', 'work'])
|
|
402
|
+
// This synthetic benchmark repository must not execute the host's personal Git hooks.
|
|
403
|
+
await runOk('git', ['-C', dest, 'config', 'core.hooksPath', '/dev/null'])
|
|
402
404
|
await runOk('git', ['-C', dest, 'config', 'user.email', 'factory-bench@local'])
|
|
403
405
|
await runOk('git', ['-C', dest, 'config', 'user.name', 'factory-bench'])
|
|
404
406
|
await runOk('git', ['-C', dest, 'add', '-A'])
|
|
@@ -21,6 +21,7 @@ describe('scratch worktrees', () => {
|
|
|
21
21
|
const output = await mkdtemp(join(tmpdir(), 'scratch-worktree-out-'))
|
|
22
22
|
roots.push(repository, output)
|
|
23
23
|
await runOk('git', ['init', '-q', '-b', 'main', repository])
|
|
24
|
+
await runOk('git', ['-C', repository, 'config', 'core.hooksPath', '/dev/null'])
|
|
24
25
|
await runOk('git', ['-C', repository, 'config', 'user.email', 'test@example.com'])
|
|
25
26
|
await runOk('git', ['-C', repository, 'config', 'user.name', 'Test'])
|
|
26
27
|
await writeFile(join(repository, 'seed.txt'), 'seed\n')
|
|
@@ -51,5 +52,5 @@ describe('scratch worktrees', () => {
|
|
|
51
52
|
await runOk('git', ['-C', repository, 'worktree', 'list', '--porcelain'])
|
|
52
53
|
).stdout
|
|
53
54
|
expect(listed.match(/^worktree /gmu)).toHaveLength(1)
|
|
54
|
-
})
|
|
55
|
+
}, 60_000)
|
|
55
56
|
})
|
|
@@ -93,9 +93,13 @@ describe('SWE worker prompts', () => {
|
|
|
93
93
|
|
|
94
94
|
describe('SWE temporary directory', () => {
|
|
95
95
|
it('stays absolute when model temperature is configured through TEMPERATURE', () => {
|
|
96
|
+
const priorTmpdir = process.env.TMPDIR
|
|
97
|
+
const priorTmp = process.env.TMP
|
|
96
98
|
const priorTemp = process.env.TEMP
|
|
97
99
|
const priorTemperature = process.env.TEMPERATURE
|
|
98
100
|
try {
|
|
101
|
+
delete process.env.TMPDIR
|
|
102
|
+
delete process.env.TMP
|
|
99
103
|
delete process.env.TEMP
|
|
100
104
|
process.env.TEMPERATURE = '0.8'
|
|
101
105
|
assert.equal(isAbsolute(absoluteSweTempDir()), true)
|
|
@@ -103,6 +107,10 @@ describe('SWE temporary directory', () => {
|
|
|
103
107
|
process.env.TEMP = '0.8'
|
|
104
108
|
assert.throws(() => absoluteSweTempDir(), /must be absolute.*TEMPERATURE/)
|
|
105
109
|
} finally {
|
|
110
|
+
if (priorTmpdir === undefined) delete process.env.TMPDIR
|
|
111
|
+
else process.env.TMPDIR = priorTmpdir
|
|
112
|
+
if (priorTmp === undefined) delete process.env.TMP
|
|
113
|
+
else process.env.TMP = priorTmp
|
|
106
114
|
if (priorTemp === undefined) delete process.env.TEMP
|
|
107
115
|
else process.env.TEMP = priorTemp
|
|
108
116
|
if (priorTemperature === undefined) delete process.env.TEMPERATURE
|
|
@@ -197,7 +205,7 @@ describe('isInsideJail (realpath containment)', () => {
|
|
|
197
205
|
symlinkSync('/etc', link)
|
|
198
206
|
// `resolveInJail` does `realpathSync(join(ws.dir, relPath))` then this containment check.
|
|
199
207
|
const real = realpathSync(join(dir, 'escape/passwd'))
|
|
200
|
-
assert.equal(real, '/etc/passwd')
|
|
208
|
+
assert.equal(real, realpathSync('/etc/passwd'))
|
|
201
209
|
assert.equal(isInsideJail(jailRoot, real), false)
|
|
202
210
|
})
|
|
203
211
|
|