vfs

package
v0.36.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 19, 2026 License: Apache-2.0 Imports: 5 Imported by: 0

Documentation

Overview

Package vfs is the single home for workspace-root containment: normalizing a candidate path against a root and rejecting anything that escapes it, symlinks included. Two callers used to carry their own copy of this logic — the local_fs agent tool (runtime/localtools) and the /files browse API (runtime/localfileservice); both now delegate here so there is exactly one symlink-escape guard to reason about.

The API is deliberately small:

  • Contain(root, candidate) resolves a path within a root and is the core primitive. It tolerates a non-existent leaf (resolving the deepest existing parent) so create/write paths validate before any I/O.
  • Within(root, abs) is the raw within-root predicate for callers that have already resolved the absolute path themselves.
  • Factory holds the serve-level allowlist of workspace roots and vends rooted Views. A session may only operate within an allowlisted root.
  • View is a Factory-vended, root-bound convenience wrapper over Contain.

Index

Constants

This section is empty.

Variables

View Source
var ErrEscape = errors.New("path escapes workspace root")

ErrEscape is returned (wrapped) when a candidate path resolves outside its root. Callers translate it into their own domain error via errors.Is.

View Source
var ErrNotAllowed = errors.New("workspace root is not allowed")

ErrNotAllowed is returned when a requested root is not in the Factory's allowlist.

Functions

func Contain

func Contain(root, candidate string) (string, error)

Contain resolves candidate (absolute, or relative to root) to a cleaned, symlink-resolved absolute path guaranteed to lie within root. Symlinks are followed so an in-sandbox link pointing outside the root is caught before any I/O. A non-existent leaf is permitted: the deepest existing parent is resolved and the missing suffix re-appended, so writing "link/new.txt" where "link" escapes is still rejected. Returns an error wrapping ErrEscape when the path leaves root.

func ResolveRoot

func ResolveRoot(root string) (string, error)

ResolveRoot returns the cleaned, absolute, symlink-resolved form of root. A non-existent root is tolerated (its cleaned absolute form is returned) so a workspace directory that has not been created yet still validates.

func Within

func Within(root, abs string) bool

Within reports whether abs lies within root. root is symlink-resolved; abs is compared as given (callers that resolved it via EvalSymlinks pass the real path). Both are made absolute first.

Types

type Factory

type Factory struct {
	// contains filtered or unexported fields
}

Factory holds the serve-level allowlist of workspace roots — the set of directories a browser client may choose as a session's workspace. Choosing a root outside the allowlist is refused. The first root is the default, used when a client asks for the sentinel "/" (or nothing), which keeps existing clients that always send cwd:"/" working.

func NewFactory

func NewFactory(roots ...string) (*Factory, error)

NewFactory builds a Factory from an ordered list of roots. The first root is the default. Roots are cleaned, made absolute, symlink-resolved, and de-duplicated (preserving first-seen order). At least one root is required.

func (*Factory) Allows

func (f *Factory) Allows(root string) (string, bool)

Allows reports whether root resolves to an allowlisted root, returning the normalized root when it does.

func (*Factory) Default

func (f *Factory) Default() string

Default returns the default root (the first configured), used for the "/" / empty sentinel.

func (*Factory) Open

func (f *Factory) Open(root string) (*View, error)

Open returns a View rooted at root, which must be allowlisted (via Resolve semantics, so "/" opens the default root).

func (*Factory) Resolve

func (f *Factory) Resolve(root string) (string, error)

Resolve maps a requested root to an allowlisted, resolved root. The sentinel "/" and the empty string both resolve to the default root — the compat story for clients (beam today) that always send cwd:"/". Any other value must resolve to a member of the allowlist; otherwise ErrNotAllowed is returned.

func (*Factory) Roots

func (f *Factory) Roots() []string

Roots returns the allowlisted roots in configured order (resolved absolute paths). The slice is a copy; callers may not mutate the Factory through it.

type View

type View struct {
	// contains filtered or unexported fields
}

View is a root-bound convenience wrapper over Contain. It caches the symlink-resolved root so repeated Resolve calls avoid re-walking it.

func OpenView

func OpenView(root string) (*View, error)

OpenView returns a View rooted at root, resolving its symlinks. Unlike Factory.Open it enforces no allowlist — use it for a single fixed root that is trusted by construction (e.g. a serve-owned browse root).

func (*View) Contains

func (v *View) Contains(abs string) bool

Contains reports whether an already-absolute path lies within the view root.

func (*View) Resolve

func (v *View) Resolve(candidate string) (string, error)

Resolve contains candidate within the view's root (see Contain).

func (*View) Root

func (v *View) Root() string

Root returns the resolved root of this view.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL