-
Notifications
You must be signed in to change notification settings - Fork 45
fix: create workspace directory at startup and default execution dir to workspace #274
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
bc1ac78
184469b
4eaa2d2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -386,17 +386,58 @@ func parseFileMode(modeStr string) os.FileMode { | |
| return os.FileMode(mode) | ||
| } | ||
|
|
||
| // setWorkspace sets the global workspace directory | ||
| func (s *Server) setWorkspace(dir string) { | ||
| // mkdirSafe creates dir and all parents within the workspace, guarding against | ||
| // symlink-based directory traversal in existing path components. | ||
| // | ||
| // os.MkdirAll follows symlinks in the existing portion of the path. If an | ||
| // attacker has placed a symlink inside the workspace that points outside (e.g. | ||
| // workspace/link -> /), MkdirAll("workspace/link/newdir") would silently create | ||
| // /newdir. mkdirSafe walks up to the deepest existing ancestor, resolves its | ||
| // symlinks, and verifies it is still inside the workspace before proceeding. | ||
| func (s *Server) mkdirSafe(dir string) error { | ||
| resolvedWorkspace, err := filepath.EvalSymlinks(s.workspaceDir) | ||
| if err != nil { | ||
| resolvedWorkspace = filepath.Clean(s.workspaceDir) | ||
| } | ||
|
|
||
| // Walk up to the deepest ancestor that already exists. | ||
| existing := dir | ||
| for { | ||
| if _, err := os.Lstat(existing); err == nil { | ||
| resolved, err := filepath.EvalSymlinks(existing) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to resolve path %q: %w", existing, err) | ||
| } | ||
| rel, relErr := filepath.Rel(resolvedWorkspace, resolved) | ||
| if relErr != nil || strings.HasPrefix(rel, ".."+string(os.PathSeparator)) || rel == ".." { | ||
| return fmt.Errorf("access denied: path %q escapes workspace jail via symlink", dir) | ||
| } | ||
| break | ||
| } | ||
| parent := filepath.Dir(existing) | ||
| if parent == existing { | ||
| // Reached filesystem root — workspace itself will be created by MkdirAll. | ||
| break | ||
| } | ||
| existing = parent | ||
| } | ||
| return os.MkdirAll(dir, 0755) | ||
| } | ||
|
|
||
| // setWorkspace sets the workspace directory and creates it if it does not exist. | ||
| func (s *Server) setWorkspace(dir string) error { | ||
| klog.Infof("setWorkspace called with dir: %q", dir) | ||
| absDir, err := filepath.Abs(dir) | ||
| if err != nil { | ||
| klog.Warningf("Failed to resolve absolute path for workspace '%s': %v", dir, err) | ||
| s.workspaceDir = dir // Fallback to provided path | ||
| } else { | ||
| s.workspaceDir = absDir | ||
| klog.Infof("Resolved workspace to absolute path: %q", s.workspaceDir) | ||
| return fmt.Errorf("failed to resolve absolute path for workspace %q: %w", dir, err) | ||
| } | ||
|
Comment on lines
428
to
433
|
||
| s.workspaceDir = absDir | ||
| klog.Infof("Resolved workspace to absolute path: %q", s.workspaceDir) | ||
| if err := os.MkdirAll(s.workspaceDir, 0755); err != nil { | ||
| return fmt.Errorf("failed to create workspace directory %q: %w", s.workspaceDir, err) | ||
| } | ||
| klog.Infof("Workspace directory ensured: %q", s.workspaceDir) | ||
| return nil | ||
|
sicaario marked this conversation as resolved.
|
||
| } | ||
|
|
||
| // sanitizePath ensures path is within allowed scope, preventing directory traversal attacks | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.