diff --git a/agentscope-harness/src/main/java/io/agentscope/harness/agent/HarnessAgentBuilderSupport.java b/agentscope-harness/src/main/java/io/agentscope/harness/agent/HarnessAgentBuilderSupport.java index 23ea83cc91..323ca4ca59 100644 --- a/agentscope-harness/src/main/java/io/agentscope/harness/agent/HarnessAgentBuilderSupport.java +++ b/agentscope-harness/src/main/java/io/agentscope/harness/agent/HarnessAgentBuilderSupport.java @@ -151,7 +151,7 @@ static AbstractFilesystem resolveFilesystem( if (b.localFilesystemSpec != null) { return b.localFilesystemSpec.toFilesystem(workspace, nsFactory); } - // Default: route through LocalFilesystemSpec so the default project (= ${user.dir}) + // Default: route through LocalFilesystemSpec so the default project (= workspace) // is overlaid below the agent workspace, matching the Claude-Code-style two-layer model. return new LocalFilesystemSpec().toFilesystem(workspace, nsFactory); } diff --git a/agentscope-harness/src/main/java/io/agentscope/harness/agent/filesystem/spec/LocalFilesystemSpec.java b/agentscope-harness/src/main/java/io/agentscope/harness/agent/filesystem/spec/LocalFilesystemSpec.java index 8e2531a208..aa601baee0 100644 --- a/agentscope-harness/src/main/java/io/agentscope/harness/agent/filesystem/spec/LocalFilesystemSpec.java +++ b/agentscope-harness/src/main/java/io/agentscope/harness/agent/filesystem/spec/LocalFilesystemSpec.java @@ -26,7 +26,6 @@ import io.agentscope.harness.agent.workspace.LocalFsMode; import io.agentscope.harness.agent.workspace.PathPolicy; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; @@ -68,9 +67,8 @@ public class LocalFilesystemSpec { * this directory and copies-on-write into the agent {@code workspace} when modified. Also * the shell {@code pwd} for {@code execute()} so command output matches user expectation. * - *
{@code null} until {@link #project(Path)} is called; defaults to - * {@link System#getProperty(String) System.getProperty("user.dir")} at - * {@link #toFilesystem} time. + *
{@code null} until {@link #project(Path)} is called; defaults to the agent
+ * {@code workspace} at {@link #toFilesystem} time.
*/
private Path project;
@@ -249,7 +247,7 @@ public LocalFilesystemSpec additionalRoots(Collection Defaults to {@code System.getProperty("user.dir")} when not set.
+ * Defaults to the agent {@code workspace} when not set.
*
* @param project project root path
* @return this spec
@@ -270,7 +268,7 @@ public LocalFilesystemSpec project(Path project) {
* @param localNamespaceFactory optional namespace factory for per-user/session folder scoping
* @return an {@link OverlayFilesystem} wired with the options in this spec
*/
- /** Project root explicitly configured, or {@code null} to fall back to {@code ${user.dir}}. */
+ /** Project root explicitly configured, or {@code null} to fall back to workspace. */
public Path getProject() {
return project;
}
@@ -286,8 +284,7 @@ public List When {@code project} is null, the lower layer should default to the agent workspace,
+ * preventing unintended exposure of the JVM process's working directory ({@code user.dir}).
+ */
+class LocalFilesystemSpecTest {
+
+ private static final RuntimeContext RT = RuntimeContext.empty();
+
+ @Test
+ void nullProject_usesWorkspaceAsLowerLayer(@TempDir Path workspace) throws Exception {
+ // Create a file in workspace that the agent should be able to read
+ Path workspaceFile = workspace.resolve("workspace-file.txt");
+ Files.writeString(workspaceFile, "content from workspace", StandardCharsets.UTF_8);
+
+ // Create spec without setting project (simulates framework default)
+ LocalFilesystemSpec spec = new LocalFilesystemSpec();
+
+ // Build filesystem
+ AbstractFilesystem fs = spec.toFilesystem(workspace, ns -> List.of("test"));
+
+ // Agent should be able to read workspace files
+ ReadResult result = fs.read(RT, workspaceFile.toAbsolutePath().toString(), 0, 0);
+ assertTrue(result.isSuccess(), "Agent should be able to read workspace files");
+ assertEquals("content from workspace", result.fileData().content());
+ }
+
+ @Test
+ void nullProject_cannotReadFilesOutsideWorkspace(@TempDir Path workspace, @TempDir Path outside)
+ throws Exception {
+ // Create a file outside workspace that the agent should NOT be able to read
+ Path outsideFile = outside.resolve("secret.txt");
+ Files.writeString(outsideFile, "secret content", StandardCharsets.UTF_8);
+
+ // Create spec without setting project
+ LocalFilesystemSpec spec = new LocalFilesystemSpec();
+
+ // Build filesystem
+ AbstractFilesystem fs = spec.toFilesystem(workspace, ns -> List.of("test"));
+
+ // Agent should NOT be able to read files outside workspace via lower layer
+ ReadResult result = fs.read(RT, outsideFile.toAbsolutePath().toString(), 0, 0);
+ assertFalse(
+ result.isSuccess(),
+ "Agent should NOT be able to read files outside workspace when project is not set");
+ }
+
+ @Test
+ void projectSet_usesProjectAsLowerLayer(@TempDir Path project, @TempDir Path workspace)
+ throws Exception {
+ // Create a file in project directory
+ Path projectFile = project.resolve("project-file.txt");
+ Files.writeString(projectFile, "content from project", StandardCharsets.UTF_8);
+
+ // Create spec with explicit project
+ LocalFilesystemSpec spec = new LocalFilesystemSpec().project(project);
+
+ // Build filesystem
+ AbstractFilesystem fs = spec.toFilesystem(workspace, ns -> List.of("test"));
+
+ // Agent should be able to read project files via lower layer
+ ReadResult result = fs.read(RT, projectFile.toAbsolutePath().toString(), 0, 0);
+ assertTrue(result.isSuccess(), "Agent should be able to read project files");
+ assertEquals("content from project", result.fileData().content());
+ }
+
+ @Test
+ void projectSet_cannotReadUserDir(@TempDir Path project, @TempDir Path workspace)
+ throws Exception {
+ // Create a file in user.dir that the agent should NOT be able to read
+ String userDir = System.getProperty("user.dir");
+ Path userDirFile = Path.of(userDir, "test-secret-userdir.txt");
+ Files.writeString(userDirFile, "secret from user.dir", StandardCharsets.UTF_8);
+ try {
+ // Create spec with explicit project (different from user.dir)
+ LocalFilesystemSpec spec = new LocalFilesystemSpec().project(project);
+
+ // Build filesystem
+ AbstractFilesystem fs = spec.toFilesystem(workspace, ns -> List.of("test"));
+
+ // Agent should NOT be able to read files from user.dir via lower layer
+ ReadResult result = fs.read(RT, userDirFile.toAbsolutePath().toString(), 0, 0);
+ assertFalse(
+ result.isSuccess(),
+ "Agent should NOT be able to read files from user.dir when project is set"
+ + " explicitly");
+ } finally {
+ Files.deleteIfExists(userDirFile);
+ }
+ }
+}