Skip to content
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

List Config dir if directory is specified #468

Merged
merged 3 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/types/custom.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -797,12 +797,16 @@ declare global {
};

type FileInfoType = {
type: string;
name: string;
size: number;
modts: number;
isdir: boolean;
perm: number;
notfound: boolean;
modestr?: string;
path?: string;
outputpos?: number;
};

type ExtBlob = Blob & {
Expand Down
49 changes: 49 additions & 0 deletions waveshell/pkg/packet/packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import (
"errors"
"fmt"
"io"
"io/fs"
"os"
"reflect"
"sync"
"time"

"github.com/wavetermdev/waveterm/waveshell/pkg/base"
"github.com/wavetermdev/waveterm/waveshell/pkg/wlog"
Expand Down Expand Up @@ -58,6 +60,7 @@ const (
WriteFileReadyPacketStr = "writefileready" // rpc-response
WriteFileDonePacketStr = "writefiledone" // rpc-response
FileDataPacketStr = "filedata"
FileStatPacketStr = "filestat"
LogPacketStr = "log" // logging packet (sent from waveshell back to server)
ShellStatePacketStr = "shellstate"

Expand Down Expand Up @@ -112,6 +115,7 @@ func init() {
TypeStrToFactory[WriteFileDonePacketStr] = reflect.TypeOf(WriteFileDonePacketType{})
TypeStrToFactory[LogPacketStr] = reflect.TypeOf(LogPacketType{})
TypeStrToFactory[ShellStatePacketStr] = reflect.TypeOf(ShellStatePacketType{})
TypeStrToFactory[FileStatPacketStr] = reflect.TypeOf(FileStatPacketType{})

var _ RpcPacketType = (*RunPacketType)(nil)
var _ RpcPacketType = (*GetCmdPacketType)(nil)
Expand Down Expand Up @@ -379,6 +383,51 @@ func MakeReInitPacket() *ReInitPacketType {
return &ReInitPacketType{Type: ReInitPacketStr}
}

type FileStatPacketType struct {
Type string `json:"type"`
Name string `json:"name"`
Size int64 `json:"size"`
ModTs time.Time `json:"modts"`
IsDir bool `json:"isdir"`
Perm int `json:"perm"`
ModeStr string `json:"modestr"`
Error string `json:"error"`
Done bool `json:"done"`
RespId string `json:"respid"`
Path string `json:"path"`
}

func (*FileStatPacketType) GetType() string {
return FileStatPacketStr
}

func (p *FileStatPacketType) GetResponseDone() bool {
return p.Done
}

func (p *FileStatPacketType) GetResponseId() string {
return p.RespId
}

func MakeFileStatPacketType() *FileStatPacketType {
return &FileStatPacketType{Type: FileStatPacketStr}
}

func MakeFileStatPacketFromFileInfo(finfo fs.FileInfo, err string, done bool) *FileStatPacketType {
resp := MakeFileStatPacketType()
resp.Error = err
resp.Done = done

resp.IsDir = finfo.IsDir()
resp.Name = finfo.Name()

resp.Size = finfo.Size()
resp.ModTs = finfo.ModTime()
resp.Perm = int(finfo.Mode().Perm())
resp.ModeStr = finfo.Mode().String()
return resp
}

type StreamFilePacketType struct {
Type string `json:"type"`
ReqId string `json:"reqid"`
Expand Down
71 changes: 70 additions & 1 deletion wavesrv/cmd/main-server.go
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,40 @@
WriteJsonSuccess(w, update)
}

func CheckIsDir(dirHandler http.Handler, fileHandler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
configPath := r.URL.Path
configAbsPath, err := filepath.Abs(configPath)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(fmt.Sprintf("error getting absolute path", err)))
return
}
configBaseDir := path.Join(scbase.GetWaveHomeDir(), "config")
configFullPath := path.Join(scbase.GetWaveHomeDir(), configAbsPath)
if !strings.HasPrefix(configFullPath, configBaseDir) {
w.WriteHeader(500)
w.Write([]byte(fmt.Sprintf("error: path is not in config folder")))
return
}
fstat, err := os.Stat(configFullPath)
if errors.Is(err, fs.ErrNotExist) {
w.WriteHeader(404)
w.Write([]byte(fmt.Sprintf("file not found: ", configAbsPath)))

Check warning

Code scanning / CodeQL

Reflected cross-site scripting Medium

Cross-site scripting vulnerability due to
user-provided value
.
return
} else if err != nil {
w.WriteHeader(500)
w.Write([]byte(fmt.Sprintf("file stat err", err)))
return
}
if fstat.IsDir() {
AuthKeyMiddleWare(dirHandler).ServeHTTP(w, r)
} else {
AuthKeyMiddleWare(fileHandler).ServeHTTP(w, r)
}
})
}

func AuthKeyMiddleWare(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
reqAuthKey := r.Header.Get("X-AuthKey")
Expand Down Expand Up @@ -857,6 +891,39 @@
})
}

func configDirHandler(w http.ResponseWriter, r *http.Request) {
log.Printf("running?")
configPath := r.URL.Path
configFullPath := path.Join(scbase.GetWaveHomeDir(), configPath)
dirFile, err := os.Open(configFullPath)
Dismissed Show dismissed Hide dismissed
if err != nil {
w.WriteHeader(500)
w.Write([]byte(fmt.Sprintf("error opening specified dir: ", err)))
return
}
entries, err := dirFile.Readdir(0)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(fmt.Sprintf("error getting files: ", err)))
return
}
var files []*packet.FileStatPacketType
for index := 0; index < len(entries); index++ {
curEntry := entries[index]
curFile := packet.MakeFileStatPacketFromFileInfo(curEntry, "", false)
files = append(files, curFile)
}
dirListJson, err := json.Marshal(files)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(fmt.Sprintf("json err: ", err)))
return
}
w.WriteHeader(200)
w.Header().Set("Content-Type", "application/json")
w.Write(dirListJson)
}

func main() {
scbase.BuildTime = BuildTime
scbase.WaveVersion = WaveVersion
Expand Down Expand Up @@ -953,7 +1020,9 @@
gr.HandleFunc("/api/write-file", AuthKeyWrap(HandleWriteFile)).Methods("POST")
configPath := path.Join(scbase.GetWaveHomeDir(), "config") + "/"
log.Printf("[wave] config path: %q\n", configPath)
gr.PathPrefix("/config/").Handler(AuthKeyMiddleWare(http.StripPrefix("/config/", http.FileServer(http.Dir(configPath)))))
isFileHandler := http.StripPrefix("/config/", http.FileServer(http.Dir(configPath)))
isDirHandler := http.HandlerFunc(configDirHandler)
gr.PathPrefix("/config/").Handler(CheckIsDir(isDirHandler, isFileHandler))

serverAddr := MainServerAddr
if scbase.IsDevMode() {
Expand Down
Loading