workspace

package
v0.4.7 Latest Latest
Warning

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

Go to latest
Published: Oct 17, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrPathTraversal = errors.New("path traversal detected")

ErrPathTraversal is returned for a path with a ".." component.

Functions

func CheckPath added in v0.4.7

func CheckPath(userPath string) (string, error)

CheckPath rejects a user-provided path that contains a ".." component and returns the path with percent-encoding fully decoded. Callers use it to answer with an error instead of quietly rewriting "../x" to "/x".

The path is URL-decoded in a loop until stable to catch double/triple-encoded traversals (%252e%252e%2f), and the check is per component, not strings.Contains, so "foo..txt" stays valid.

func DirMode added in v0.4.7

func DirMode() os.FileMode

DirMode is the mode for session directories: 0o700 when the sandbox user owns them, otherwise 0o777 so a differently-numbered sandbox user (e.g. "nobody", UID 65534) can write. In the 0o777 case the parent WORKSPACE_BASE_DIR is created 0o700 so other host users cannot reach the session directories.

func PrivateDirs added in v0.4.7

func PrivateDirs() bool

PrivateDirs reports whether session directories are owned by the sandbox user and can therefore be created owner-only.

func SandboxUser added in v0.4.7

func SandboxUser() string

SandboxUser returns the user ("uid:gid" or a name) sandbox processes run as.

By default sandboxes run as the same uid:gid as the API process. They then own the bind-mounted session directory, which can stay 0o700. If the API runs as root (not the Compose default) there is no private uid to share, so the legacy "nobody" user is used together with world-writable (0o777) session directories. SANDBOX_USER overrides the choice; any value other than the API's own uid:gid keeps the 0o777 directories because the user cannot be assumed to own them.

Types

type Manager

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

func New

func New(baseDir string) *Manager

func (*Manager) Create

func (m *Manager) Create(sessionID uuid.UUID) (string, error)

Create initializes an isolated workspace directory for a session.

os.MkdirAll honours the process umask, which can strip permission bits; the explicit os.Chmod afterwards is umask-independent.

func (*Manager) CreateSnapshot

func (m *Manager) CreateSnapshot(sessionID, snapshotID uuid.UUID) (archivePath string, sizeBytes int64, err error)

CreateSnapshot creates a gzip-compressed tar archive of the session workspace. The archive is written to {baseDir}/snapshots/<snapshotID>.tar.gz and the archive path + uncompressed size are returned.

func (*Manager) Delete

func (m *Manager) Delete(sessionID uuid.UUID) error

Delete removes the workspace directory.

func (*Manager) DeleteSnapshot

func (m *Manager) DeleteSnapshot(archivePath string) error

DeleteSnapshot removes a snapshot archive from disk.

func (*Manager) Exists added in v0.3.2

func (m *Manager) Exists(sessionID uuid.UUID, userPath string) (bool, error)

Exists reports whether userPath refers to a real file/dir inside the session workspace. Symlinks that escape the workspace root are treated as absent (not an oracle for host paths).

func (*Manager) OpenFile

func (m *Manager) OpenFile(sessionID uuid.UUID, userPath string) (*os.File, error)

OpenFile opens a file inside the workspace for reading. Symlink targets are validated to remain inside the workspace.

func (*Manager) RestoreSnapshot

func (m *Manager) RestoreSnapshot(sessionID uuid.UUID, archivePath string) error

RestoreSnapshot extracts a snapshot archive into the session workspace. Files in the archive overwrite existing files; the workspace is not cleared first so existing files not in the snapshot are preserved.

func (*Manager) SafePath

func (m *Manager) SafePath(sessionID uuid.UUID, userPath string) (string, error)

SafePath resolves a user-provided path within the workspace, enforcing:

  1. URL-decoded (loop until stable) to catch double/triple-encoded traversals
  2. No ".." path components (checked after full decoding, before Clean)
  3. Resolved path stays inside the workspace root (filepath.Clean + HasPrefix)

func (*Manager) SessionDirs added in v0.4.1

func (m *Manager) SessionDirs() ([]uuid.UUID, error)

SessionDirs returns the session IDs that have a directory under the base directory. Entries whose name is not a UUID (snapshots/, warm pool) are skipped.

func (*Manager) SessionPath

func (m *Manager) SessionPath(sessionID uuid.UUID) string

func (*Manager) WriteFile

func (m *Manager) WriteFile(sessionID uuid.UUID, userPath string, r io.Reader, maxBytes int64) (int64, error)

WriteFile writes data to a safe path inside the workspace, creating intermediate directories as needed.

Security: guards against symlink-escape attacks where a container process creates a symlink inside /workspace pointing to a host path outside the workspace root. Two layers of protection are applied:

  1. The entire parent-directory chain is symlink-resolved and verified to stay inside the workspace root before any write takes place.
  2. The file is opened with O_NOFOLLOW so that if the final path component is itself a symlink (e.g. created in a TOCTOU window) the open fails.

Permissions: directories use DirMode and files 0o644. os.Chmod is called after creation to bypass the process umask.

Jump to

Keyboard shortcuts

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