fix(sandbox): honour configured timeoutSeconds in DockerSandboxProvider.executeCode - #3567
Conversation
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
PR Review Summary
Verdict: AI review comments are untrusted advisory output. The summary reports workflow-generated completion status only, not model-authored pass/fail claims. |
There was a problem hiding this comment.
Pull request overview
Fixes the TypeScript DockerSandboxProvider so executeCode() honors the per-session timeoutSeconds configured at createSession, restoring the sandbox timeout as an effective safety control (issue #3118).
Changes:
- Track per-session exec timeout (ms) when creating a session and use it in
executeCode()instead of a hard-coded60_000. - Clean up the per-session timeout entry in
destroySession. - Add Jest regression tests that mock
child_processto validate timeout propagation without requiring Docker.
TL;DR: 2 blockers, 0 warnings. Fix #1 and #2 and this ships.
| # | Sev | Issue | Where |
|---|---|---|---|
| 1 | Block | New test file license header format doesn’t match repo’s standard two-line MIT header | tests/sandbox-timeout.test.ts:1 |
| 2 | Block | timeoutSeconds is used without validating finite/positive value; can become NaN/0 at runtime and undermine the timeout safety control |
src/sandbox.ts:193 |
#1: Update the header to the two-line // Copyright... + // Licensed... format.
#2: Guard/fallback when timeoutSeconds isn’t a positive finite number before storing milliseconds.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| agent-governance-typescript/src/sandbox.ts | Stores per-session timeout (ms) and uses it in execFile options; cleans up timeout tracking on session destroy. |
| agent-governance-typescript/tests/sandbox-timeout.test.ts | Adds regression tests asserting executeCode passes the session timeout (or default) to execFile. |
| @@ -0,0 +1,71 @@ | |||
| // Copyright (c) Microsoft Corporation. Licensed under the MIT License. | |||
| .trim(); | ||
|
|
||
| this.containers.set(sessionId, containerId); | ||
| this.sessionTimeoutsMs.set(sessionId, cfg.timeoutSeconds * 1000); |
MohammadHaroonAbuomar
left a comment
There was a problem hiding this comment.
Minor:
- the ?? defaultSandboxConfig() fallback on the same line is dead in-process; harmless, fine to leave.
| // Previously this was hard-coded to 60_000 ms, so a custom timeoutSeconds | ||
| // was silently ignored and the sandbox ran longer than the caller allowed. | ||
| const timeoutMs = | ||
| this.sessionTimeoutsMs.get(sessionId) ?? defaultSandboxConfig().timeoutSeconds * 1000; |
There was a problem hiding this comment.
timeoutSeconds <= 0 now yields timeout: 0, which Node execFile treats as NO timeout (unlimited execution); on main the same degenerate config still got the 60s cap. Clamp to the default when <= 0 (config is operator input, but the regression is real).
Address review on microsoft#3567: - Guard the configured timeoutSeconds via resolveTimeoutMs(): fall back to the default when it is not a positive, finite number. Node's execFile treats timeout: 0 (or NaN) as "no timeout", so a degenerate value (<= 0, or one that arrived via any) would silently disable the sandbox timeout safety control. Applied at the session-store site so the tracked value is always valid. - Fix the new test file's MIT header to the repo's two-line // Copyright / // Licensed format. Added regression tests for the non-positive and non-finite cases. Signed-off-by: Pratik Dhanave <i.pratikdhanave@gmail.com>
DockerSandboxProvider.executeCode() always passed { timeout: 60_000 } to
execFile, ignoring the timeoutSeconds supplied to createSession. A caller
that set e.g. timeoutSeconds: 2 to bound how long agent-generated code may
run got 60s instead — a silently weakened sandbox safety control.
Record each session's timeout (timeoutSeconds * 1000) when the session is
created and use it in executeCode, falling back to the default only for
sessions created before the timeout was tracked. The per-session entry is
cleaned up in destroySession alongside the container mapping.
Fixes microsoft#3118
Signed-off-by: Pratik Dhanave <i.pratikdhanave@gmail.com>
Address review on microsoft#3567: - Guard the configured timeoutSeconds via resolveTimeoutMs(): fall back to the default when it is not a positive, finite number. Node's execFile treats timeout: 0 (or NaN) as "no timeout", so a degenerate value (<= 0, or one that arrived via any) would silently disable the sandbox timeout safety control. Applied at the session-store site so the tracked value is always valid. - Fix the new test file's MIT header to the repo's two-line // Copyright / // Licensed format. Added regression tests for the non-positive and non-finite cases. Signed-off-by: Pratik Dhanave <i.pratikdhanave@gmail.com>
99bf334 to
fd1a713
Compare
|
Thanks MohammadHaroonAbuomar and Copilot — both blockers addressed in the latest push:
Added regression tests for the non-positive ( |
Summary
DockerSandboxProvider.executeCode()always passed{ timeout: 60_000 }toexecFile, ignoring thetimeoutSecondsa caller supplied tocreateSession. Someone who set e.g.timeoutSeconds: 2to bound how long agent-generated code may run actually got 60 seconds — a silently weakened sandbox safety control. Since timeouts are part of the sandbox's safety envelope, this can cause unexpected resource usage and gives a false sense of containment.Fixes #3118
Change
timeoutSeconds * 1000) in asessionId -> msmap when the session is created.executeCodeinstead of the hard-coded60_000, falling back todefaultSandboxConfig().timeoutSeconds * 1000only for sessions created before the timeout was tracked.destroySession, alongside the existing container mapping, so the map does not leak.Testing
Added
tests/sandbox-timeout.test.ts(mockschild_process, so it runs without Docker):executeCodeuses the session-configuredtimeoutSeconds(2→2000ms).60_000(5→5000ms).60_000ms) when no config is provided.Verified the tests fail on the pre-fix code (
Expected: 2000 / Received: 60000) and pass with the fix.tscbuild is clean,eslintis clean on the changed files, and the full jest suite passes (the one unrelated failure is the pre-existing Docker lifecycle test, which fails identically onmainin an environment without a working Docker daemon and otherwise skips).