Skip to content

fix(sandbox): honour configured timeoutSeconds in DockerSandboxProvider.executeCode - #3567

Open
PratikDhanave (PratikDhanave) wants to merge 2 commits into
microsoft:mainfrom
PratikDhanave:fix/sandbox-honour-timeout-seconds
Open

fix(sandbox): honour configured timeoutSeconds in DockerSandboxProvider.executeCode#3567
PratikDhanave (PratikDhanave) wants to merge 2 commits into
microsoft:mainfrom
PratikDhanave:fix/sandbox-honour-timeout-seconds

Conversation

@PratikDhanave

Copy link
Copy Markdown

Summary

DockerSandboxProvider.executeCode() always passed { timeout: 60_000 } to execFile, ignoring the timeoutSeconds a caller supplied to createSession. Someone who set e.g. timeoutSeconds: 2 to 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

  • Record each session's timeout (timeoutSeconds * 1000) in a sessionId -> ms map when the session is created.
  • Use that value in executeCode instead of the hard-coded 60_000, falling back to defaultSandboxConfig().timeoutSeconds * 1000 only for sessions created before the timeout was tracked.
  • Clean up the per-session entry in destroySession, alongside the existing container mapping, so the map does not leak.

Testing

Added tests/sandbox-timeout.test.ts (mocks child_process, so it runs without Docker):

  • executeCode uses the session-configured timeoutSeconds (22000 ms).
  • A custom timeout is not hard-coded to 60_000 (55000 ms).
  • Falls back to the default (60_000 ms) when no config is provided.

Verified the tests fail on the pre-fix code (Expected: 2000 / Received: 60000) and pass with the fix. tsc build is clean, eslint is 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 on main in an environment without a working Docker daemon and otherwise skips).

Copilot AI review requested due to automatic review settings August 1, 2026 04:45
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown

PR Review Summary

Check Status Details
🔍 Code Review ⚠️ Missing No current-run comment
🛡️ Security Scan ⚠️ Missing No current-run comment
🔄 Breaking Changes ⚠️ Missing No current-run comment
📝 Docs Sync ⚠️ Missing No current-run comment
🧪 Test Coverage ⚠️ Missing No current-run comment

Verdict: ⚠️ AI review incomplete; ready for human review

AI review comments are untrusted advisory output. The summary reports workflow-generated completion status only, not model-authored pass/fail claims.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-coded 60_000.
  • Clean up the per-session timeout entry in destroySession.
  • Add Jest regression tests that mock child_process to 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 MohammadHaroonAbuomar left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

PratikDhanave (PratikDhanave) added a commit to PratikDhanave/agent-governance-toolkit that referenced this pull request Aug 21, 2026
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>
@PratikDhanave
PratikDhanave (PratikDhanave) force-pushed the fix/sandbox-honour-timeout-seconds branch from 99bf334 to fd1a713 Compare August 21, 2026 03:06
@PratikDhanave

Copy link
Copy Markdown
Author

Thanks MohammadHaroonAbuomar and Copilot — both blockers addressed in the latest push:

  1. Test license header → now the two-line // Copyright (c) Microsoft Corporation. / // Licensed under the MIT License. format matching the rest of the TS codebase.
  2. timeoutSeconds validation → added resolveTimeoutMs(), which falls back to the default when the value isn't a positive, finite number, applied at the session-store site so the tracked value is always valid. This closes the timeoutSeconds <= 0timeout: 0 (unlimited execution) regression you both flagged — a non-positive or non-finite config can no longer disable the sandbox cap.

Added regression tests for the non-positive (0) and non-finite (NaN) cases; tsc, eslint, and jest (5/5) are green. Commits are signed off for DCO. PTAL.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/M Medium PR (< 200 lines) tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: TypeScript Docker sandbox ignores custom timeoutSeconds - SandboxConfig.timeoutSeconds

3 participants