Skip to content

[Bug] CompositeFilesystem root ls/grep escape the workspace under the UNRESTRICTED default backend (list_files(".") enumerates the drive root) #3253

Description

@aiyili

Summary

CompositeFilesystem routes root-level ls/grep operations to the default backend with an absolute / path, and the UNRESTRICTED resolver passes absolute paths through unchanged — so the operation anchors at the OS root instead of the workspace root. In the default RemoteFilesystemSpec configuration, list_files(".") from the model enumerates the drive root (C:\ top level on Windows), breaking workspace containment.

This is point 4 of the #3245 evaluation ("list_files('.') can enumerate outside the workspace (drive root), since no containment check exists on that path"), which was deliberately tracked separately from PR #3247 (that PR blocks traversal only for opt-in sharedLocalWorkspace users; the default namespaced backend is still UNRESTRICTED).

Framing correction (per evaluation): RemoteFilesystemSpec is itself opt-in (it throws at build time without a distributed store); the harness default is LocalFilesystemSpec (ROOTED), where ls("/") is denied. The accurate scope is configuration-gated host-root disclosure via the UNRESTRICTED backend mode — and the escape is namespace-independent (/ is exempt from prefixing). Ordinary absolute paths passing through the same backend are tracked separately in #3258.

Root cause (verified against main @ ed77530)

  • CompositeFilesystem.ls — agentscope-harness/.../CompositeFilesystem.java:199-201: if ("/".equals(path) || ".".equals(path)) { ... defaultBackend.ls(runtimeContext, "/") ... }
  • CompositeFilesystem.grep — :280-282: the root branch passes path through verbatim to defaultBackend.grep(...); a "/" path hits the same passthrough.
  • LocalFilesystem.resolveUnrestricted — LocalFilesystem.java:681-687: absolute paths are returned unchanged.
  • LocalFilesystem.glob is already anchored (resolvePath(".")) — only ls and grep("/") are affected.

Proposed fix

Route the default backend's root operations through the backend's own root anchor: pass a blank path instead of "/". LocalFilesystem.resolvePath already resolves a blank key to the backend cwd (LocalFilesystem.java:598-600) and applyNamespacePrefix leaves blank keys unprefixed (:691-693) — a one-line change per site. Shared-route backends keep their "/" (a virtual store root for them). No change to per-user namespace isolation for ordinary (non-root) paths.

Tests required (per the #3245 follow-up evaluation)

  • a .// listing cannot escape the workspace in both spec modes (namespaced default backend and sharedLocalWorkspace=true)
  • grep_files(path="/") is contained the same way
  • per-user namespace isolation for ordinary paths is unchanged (existing tests pin this and must keep passing)

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/harnessagentscope-harness (test/runtime support)bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions