Skip to content

fix(harness): fix concurrent sandbox isolation in multi-user singleton deployments#1964

Open
Buktal wants to merge 1 commit into
agentscope-ai:mainfrom
Buktal:fix/sandbox-concurrent-isolation
Open

fix(harness): fix concurrent sandbox isolation in multi-user singleton deployments#1964
Buktal wants to merge 1 commit into
agentscope-ai:mainfrom
Buktal:fix/sandbox-concurrent-isolation

Conversation

@Buktal

@Buktal Buktal commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

AgentScope-Java Version

2.0.0-SNAPSHOT

Description

Fixes two related concurrency bugs in HarnessAgent when used as a singleton serving concurrent sessions (the standard multi-tenant deployment pattern documented in the class Javadoc and official examples).

Bug 1 — SandboxBackedFilesystem cross-session contamination (#1896)

SandboxBackedFilesystem held a single volatile Sandbox sandbox field, and SandboxLifecycleMiddleware held a single AtomicReference<SandboxAcquireResult>. Concurrent calls from different sessions overwrote each other's bindings — User A's tools could execute inside User B's sandbox.

Fix: replace both single-value fields with ConcurrentHashMap<sessionId, ...>. SandboxAware interface updated: setSandbox/getSandbox replaced by bindSandbox(key, sandbox), unbindSandbox(key), and getSandbox(key). The three public methods on SandboxBackedFilesystem already receive RuntimeContext, so the session key is always available at call time.

Bug 2 — WorkspaceMessageBus / WorkspaceAsyncToolRegistry using sandbox filesystem

HarnessAgent.build() constructed both components after replacing filesystem with SandboxBackedFilesystem. This caused their internal RuntimeContext.empty() file operations to call requireSandbox(null), which would throw or contaminate sandbox state.

Fix: capture busFilesystem before the sandbox replacement so bus and registry always use the non-sandbox (local or remote) filesystem. These are process-scoped infrastructure components with their own path-based isolation; they must not go through the sandbox.

Testing

  • SandboxBackedFilesystemTest: updated existing 3 tests to new API; added 3 new tests including a concurrent 8-session isolation test using CountDownLatch
  • CompactionMiddlewareTest: already existed on the feat/compaction-events branch; no changes needed for this fix

Closes #1896

Checklist

  • Code has been formatted with mvn spotless:apply
  • All tests are passing (mvn test)
  • Javadoc comments are complete and follow project conventions
  • Related documentation has been updated (e.g. links, examples, etc.)
  • Code is ready for review

…n deployments

Two related bugs in HarnessAgent when used as a singleton serving concurrent sessions:

Bug 1: SandboxBackedFilesystem used a single volatile Sandbox field and
SandboxLifecycleMiddleware used a single AtomicReference<SandboxAcquireResult>.
Concurrent sessions overwrote each other's bindings, causing cross-user sandbox
contamination. Fixed by replacing both with ConcurrentHashMap<sessionId, ...>,
keyed by sessionId so each session gets an isolated slot. SandboxAware interface
updated: setSandbox/getSandbox replaced by bindSandbox/unbindSandbox/getSandbox(key).

Bug 2: WorkspaceMessageBus and WorkspaceAsyncToolRegistry were constructed with
the post-sandbox filesystem (SandboxBackedFilesystem) in HarnessAgent.build().
These are process-scoped infrastructure components; routing them through the
sandbox filesystem causes RuntimeContext.empty() calls to fail or contaminate
sandbox state. Fixed by capturing busFilesystem before the sandbox replacement
and using it exclusively for bus/registry construction.

Closes agentscope-ai#1896
@Buktal Buktal requested a review from a team June 29, 2026 13:25
@codecov

codecov Bot commented Jun 29, 2026

Copy link
Copy Markdown

@itxaiohanglover itxaiohanglover 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.

Good architectural fix — separating bus/registry filesystem from the sandbox filesystem prevents accidental routing through SandboxBackedFilesystem. The per-session ConcurrentHashMap approach for sandbox bindings looks correct for concurrent isolation. One question: is there a cleanup path for stale session entries when a session ends, or do entries persist for the proxy's lifetime?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]:SandboxBackedFilesystem 并发多用户不安全 &&框架后台任务用 RuntimeContext.empty() 写文件系统,破坏多用户路由

2 participants