Add show_page and show_file, and let file tools reach any path
This commit is contained in:
+22
-57
@@ -33,19 +33,9 @@ func (s *Server) GetTaskDir(w http.ResponseWriter, r *http.Request, taskID strin
|
||||
if params.Path != nil {
|
||||
rel = *params.Path
|
||||
}
|
||||
name, ok := worktreeName(w, rel)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
name, full := taskPath(t.WorktreePath, rel)
|
||||
|
||||
root, err := os.OpenRoot(t.WorktreePath)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
defer root.Close()
|
||||
|
||||
f, err := root.Open(name)
|
||||
f, err := os.Open(full)
|
||||
if err != nil {
|
||||
writePathError(w, err)
|
||||
return
|
||||
@@ -73,7 +63,7 @@ func (s *Server) GetTaskDir(w http.ResponseWriter, r *http.Request, taskID strin
|
||||
if de.Name() == ".git" {
|
||||
continue
|
||||
}
|
||||
entries = append(entries, dirEntry(root, path.Join(name, de.Name()), de))
|
||||
entries = append(entries, dirEntry(filepath.Join(full, de.Name()), de))
|
||||
}
|
||||
slices.SortFunc(entries, func(a, b api.DirEntry) int {
|
||||
if (a.Type == api.DirEntryTypeDir) != (b.Type == api.DirEntryTypeDir) {
|
||||
@@ -94,11 +84,15 @@ func (s *Server) GetTaskDir(w http.ResponseWriter, r *http.Request, taskID strin
|
||||
listing.Truncated = true
|
||||
}
|
||||
|
||||
real, err := realDir(t.WorktreePath, name)
|
||||
real, err := realDir(t.WorktreePath, full)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
if !filepath.IsLocal(real) {
|
||||
writeJSON(w, http.StatusOK, listing)
|
||||
return
|
||||
}
|
||||
paths := []string{}
|
||||
for _, e := range listing.Entries {
|
||||
paths = append(paths, path.Join(real, e.Name))
|
||||
@@ -114,12 +108,12 @@ func (s *Server) GetTaskDir(w http.ResponseWriter, r *http.Request, taskID strin
|
||||
writeJSON(w, http.StatusOK, listing)
|
||||
}
|
||||
|
||||
func realDir(worktree, name string) (string, error) {
|
||||
func realDir(worktree, full string) (string, error) {
|
||||
root, err := filepath.EvalSymlinks(worktree)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
dir, err := filepath.EvalSymlinks(filepath.Join(worktree, name))
|
||||
dir, err := filepath.EvalSymlinks(full)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -130,14 +124,14 @@ func realDir(worktree, name string) (string, error) {
|
||||
return filepath.ToSlash(rel), nil
|
||||
}
|
||||
|
||||
func dirEntry(root *os.Root, name string, de fs.DirEntry) api.DirEntry {
|
||||
func dirEntry(full string, de fs.DirEntry) api.DirEntry {
|
||||
e := api.DirEntry{Name: de.Name(), Type: api.DirEntryTypeOther}
|
||||
|
||||
var fi fs.FileInfo
|
||||
var err error
|
||||
if de.Type()&fs.ModeSymlink != 0 {
|
||||
e.Symlink = true
|
||||
fi, err = root.Stat(name)
|
||||
fi, err = os.Stat(full)
|
||||
} else {
|
||||
fi, err = de.Info()
|
||||
}
|
||||
@@ -164,19 +158,9 @@ func (s *Server) GetTaskFile(w http.ResponseWriter, r *http.Request, taskID stri
|
||||
writeError(w, http.StatusBadRequest, errors.New("path is required"))
|
||||
return
|
||||
}
|
||||
name, ok := worktreeName(w, params.Path)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
name, full := taskPath(t.WorktreePath, params.Path)
|
||||
|
||||
root, err := os.OpenRoot(t.WorktreePath)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
defer root.Close()
|
||||
|
||||
fi, err := root.Stat(name)
|
||||
fi, err := os.Stat(full)
|
||||
if err != nil {
|
||||
writePathError(w, err)
|
||||
return
|
||||
@@ -186,7 +170,7 @@ func (s *Server) GetTaskFile(w http.ResponseWriter, r *http.Request, taskID stri
|
||||
return
|
||||
}
|
||||
|
||||
f, err := root.Open(name)
|
||||
f, err := os.Open(full)
|
||||
if err != nil {
|
||||
writePathError(w, err)
|
||||
return
|
||||
@@ -225,19 +209,9 @@ func (s *Server) GetTaskRaw(w http.ResponseWriter, r *http.Request, taskID strin
|
||||
writeError(w, http.StatusBadRequest, errors.New("path is required"))
|
||||
return
|
||||
}
|
||||
name, ok := worktreeName(w, params.Path)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
name, full := taskPath(t.WorktreePath, params.Path)
|
||||
|
||||
root, err := os.OpenRoot(t.WorktreePath)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
defer root.Close()
|
||||
|
||||
f, err := root.Open(name)
|
||||
f, err := os.Open(full)
|
||||
if err != nil {
|
||||
writePathError(w, err)
|
||||
return
|
||||
@@ -265,21 +239,12 @@ func (s *Server) GetTaskRaw(w http.ResponseWriter, r *http.Request, taskID strin
|
||||
http.ServeContent(w, r, "", fi.ModTime(), f)
|
||||
}
|
||||
|
||||
func worktreeName(w http.ResponseWriter, rel string) (string, bool) {
|
||||
if rel == "" {
|
||||
return ".", true
|
||||
func taskPath(worktree, p string) (string, string) {
|
||||
name := filepath.Clean(filepath.FromSlash(p))
|
||||
if filepath.IsAbs(name) {
|
||||
return filepath.ToSlash(name), name
|
||||
}
|
||||
name := filepath.FromSlash(rel)
|
||||
if !filepath.IsLocal(name) {
|
||||
writeError(w, http.StatusBadRequest, fmt.Errorf("%s is not a path inside the worktree", rel))
|
||||
return "", false
|
||||
}
|
||||
name = filepath.ToSlash(filepath.Clean(name))
|
||||
if slices.Contains(strings.Split(name, "/"), ".git") {
|
||||
writeError(w, http.StatusBadRequest, fmt.Errorf("%s is inside .git", rel))
|
||||
return "", false
|
||||
}
|
||||
return name, true
|
||||
return filepath.ToSlash(name), filepath.Join(worktree, name)
|
||||
}
|
||||
|
||||
func writePathError(w http.ResponseWriter, err error) {
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"g.fc.run/ai/byre/internal/agents"
|
||||
"g.fc.run/ai/byre/internal/api"
|
||||
)
|
||||
|
||||
@@ -28,17 +27,6 @@ func (s *Server) GetTaskImage(w http.ResponseWriter, r *http.Request, taskID str
|
||||
return
|
||||
}
|
||||
|
||||
root, err := filepath.EvalSymlinks(t.WorktreePath)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
media, err := filepath.EvalSymlinks(agents.MediaDir(t.WorktreePath))
|
||||
if err != nil && !errors.Is(err, fs.ErrNotExist) {
|
||||
writeError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
path := params.Path
|
||||
if !filepath.IsAbs(path) {
|
||||
path = filepath.Join(t.WorktreePath, path)
|
||||
@@ -53,11 +41,6 @@ func (s *Server) GetTaskImage(w http.ResponseWriter, r *http.Request, taskID str
|
||||
return
|
||||
}
|
||||
|
||||
if !within(root, real) && (media == "" || !within(media, real)) {
|
||||
writeError(w, http.StatusForbidden, fmt.Errorf("%s is outside the task's worktree and media folder", params.Path))
|
||||
return
|
||||
}
|
||||
|
||||
contentType := worktreeImageTypes[strings.ToLower(filepath.Ext(real))]
|
||||
if contentType == "" {
|
||||
writeError(w, http.StatusBadRequest, fmt.Errorf("%s is not a PNG, JPEG, GIF, WebP or SVG file", params.Path))
|
||||
@@ -87,8 +70,3 @@ func (s *Server) GetTaskImage(w http.ResponseWriter, r *http.Request, taskID str
|
||||
w.Header().Set("Cache-Control", "private, no-cache")
|
||||
http.ServeContent(w, r, "", info.ModTime(), f)
|
||||
}
|
||||
|
||||
func within(root, path string) bool {
|
||||
rel, err := filepath.Rel(root, path)
|
||||
return err == nil && rel != ".." && !strings.HasPrefix(rel, ".."+string(filepath.Separator))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user