From: Jeff Halter Date: Mon, 31 Jan 2022 04:56:04 +0000 (-0800) Subject: Sanitize file path input to prevent directory traversal X-Git-Url: https://git.r.bdr.sh/rbdr/mobius/commitdiff_plain/92a7e455a347e5be7fb69b6846b9f27ca698ae12?ds=inline;hp=-c Sanitize file path input to prevent directory traversal --- 92a7e455a347e5be7fb69b6846b9f27ca698ae12 diff --git a/hotline/file_path.go b/hotline/file_path.go index d6c05bc..2e2e085 100644 --- a/hotline/file_path.go +++ b/hotline/file_path.go @@ -32,6 +32,9 @@ type FilePath struct { const minFilePathLen = 2 func (fp *FilePath) UnmarshalBinary(b []byte) error { + if b == nil { + return nil + } if len(b) < minFilePathLen { return errors.New("insufficient bytes") } @@ -71,3 +74,21 @@ func ReadFilePath(filePathFieldData []byte) string { } return fp.String() } + +func readPath(fileRoot string, filePath, fileName []byte) (fullPath string, err error) { + var fp FilePath + if filePath != nil { + if err = fp.UnmarshalBinary(filePath); err != nil { + return "", err + } + } + + fullPath = path.Join( + "/", + fileRoot, + fp.String(), + path.Join("/", string(fileName)), + ) + + return fullPath, nil +} diff --git a/hotline/file_path_test.go b/hotline/file_path_test.go index a8ab2ce..4c2f6db 100644 --- a/hotline/file_path_test.go +++ b/hotline/file_path_test.go @@ -6,10 +6,6 @@ import ( ) func TestFilePath_UnmarshalBinary(t *testing.T) { - type fields struct { - ItemCount []byte - Items []FilePathItem - } type args struct { b []byte } @@ -58,3 +54,103 @@ func TestFilePath_UnmarshalBinary(t *testing.T) { }) } } + +func Test_readPath(t *testing.T) { + type args struct { + fileRoot string + filePath []byte + fileName []byte + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "when filePath is invalid", + args: args{ + fileRoot: "/usr/local/var/mobius/Files", + filePath: []byte{ + 0x61, + }, + fileName: []byte{ + 0x61, 0x61, 0x61, + }, + }, + want: "", + wantErr: true, + }, + { + name: "when filePath is nil", + args: args{ + fileRoot: "/usr/local/var/mobius/Files", + filePath: nil, + fileName: []byte("foo"), + + }, + want: "/usr/local/var/mobius/Files/foo", + }, + { + name: "when fileName contains .. ", + args: args{ + fileRoot: "/usr/local/var/mobius/Files", + filePath: nil, + fileName: []byte("../../../foo"), + }, + want: "/usr/local/var/mobius/Files/foo", + }, + { + name: "when filePath contains .. ", + args: args{ + fileRoot: "/usr/local/var/mobius/Files", + filePath: []byte{ + 0x00, 0x02, + 0x00, 0x00, + 0x03, + 0x2e, 0x2e, 0x2f, + 0x00, 0x00, + 0x08, + 0x41, 0x20, 0x53, 0x75, 0x62, 0x44, 0x69, 0x72, + }, + fileName: []byte("foo"), + }, + want: "/usr/local/var/mobius/Files/A SubDir/foo", + }, + { + name: "when a filePath entry contains .. ", + args: args{ + fileRoot: "/usr/local/var/mobius/Files", + filePath: []byte{ + 0x00, 0x01, + 0x00, 0x00, + 0x0b, + 0x2e, 0x2e, 0x2f, 0x41, 0x20, 0x53, 0x75, 0x62, 0x44, 0x69, 0x72, + }, + fileName: []byte("foo"), + }, + want: "/usr/local/var/mobius/Files/A SubDir/foo", + }, + { + name: "when filePath and fileName are nil", + args: args{ + fileRoot: "/usr/local/var/mobius/Files", + filePath: nil, + fileName: nil, + }, + want: "/usr/local/var/mobius/Files", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := readPath(tt.args.fileRoot, tt.args.filePath, tt.args.fileName) + if (err != nil) != tt.wantErr { + t.Errorf("readPath() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("readPath() got = %v, want %v", got, tt.want) + } + }) + } +} \ No newline at end of file diff --git a/hotline/flattened_file_object.go b/hotline/flattened_file_object.go index 656cdd3..02804f6 100644 --- a/hotline/flattened_file_object.go +++ b/hotline/flattened_file_object.go @@ -2,7 +2,6 @@ package hotline import ( "encoding/binary" - "fmt" "os" ) @@ -197,8 +196,12 @@ func (f flattenedFileObject) BinaryMarshal() []byte { return out } -func NewFlattenedFileObject(filePath, fileName string) (*flattenedFileObject, error) { - file, err := os.Open(fmt.Sprintf("%v/%v", filePath, fileName)) +func NewFlattenedFileObject(fileRoot string, filePath, fileName []byte) (*flattenedFileObject, error) { + fullFilePath, err := readPath(fileRoot, filePath, fileName) + if err != nil { + return nil, err + } + file, err := os.Open(fullFilePath) if err != nil { return nil, err } @@ -214,7 +217,7 @@ func NewFlattenedFileObject(filePath, fileName string) (*flattenedFileObject, er return &flattenedFileObject{ FlatFileHeader: NewFlatFileHeader(), - FlatFileInformationFork: NewFlatFileInformationFork(fileName), + FlatFileInformationFork: NewFlatFileInformationFork(string(fileName)), FlatFileDataForkHeader: FlatFileDataForkHeader{ ForkType: []byte("DATA"), CompressionType: []byte{0, 0, 0, 0}, diff --git a/hotline/flattened_file_object_test.go b/hotline/flattened_file_object_test.go index 19b7c94..df05c09 100644 --- a/hotline/flattened_file_object_test.go +++ b/hotline/flattened_file_object_test.go @@ -3,8 +3,9 @@ package hotline import ( "bytes" "encoding/hex" - "github.com/davecgh/go-spew/spew" - "reflect" + "fmt" + "github.com/stretchr/testify/assert" + "os" "testing" ) @@ -20,75 +21,56 @@ func TestReadFlattenedFileObject(t *testing.T) { } } -// -//func TestNewFlattenedFileObject(t *testing.T) { -// ffo := NewFlattenedFileObject("test/config/files", "testfile.txt") -// -// dataSize := ffo.FlatFileDataForkHeader.DataSize -// want := []byte{0, 0, 0, 0x17} -// if bytes.Compare(dataSize, want) != 0 { -// t.Errorf("%q, want %q", dataSize, want) -// } -// -// comment := ffo.FlatFileInformationFork.Comment -// want = []byte("Test Comment") -// if bytes.Compare(ffo.FlatFileInformationFork.Comment, want) != 0 { -// t.Errorf("%q, want %q", comment, want) -// } -//} - func TestNewFlattenedFileObject(t *testing.T) { type args struct { - filePath string - fileName string + fileRoot string + filePath []byte + fileName []byte } tests := []struct { name string args args want *flattenedFileObject - wantErr bool + wantErr assert.ErrorAssertionFunc }{ { - name: "when file path is valid", + name: "with valid file", args: args{ - filePath: "./test/config/Files/", - fileName: "testfile.txt", + fileRoot: func() string { path, _ := os.Getwd(); return path + "/test/config/Files" }(), + fileName: []byte("testfile.txt"), + filePath: []byte{0, 0}, }, want: &flattenedFileObject{ FlatFileHeader: NewFlatFileHeader(), FlatFileInformationForkHeader: FlatFileInformationForkHeader{}, FlatFileInformationFork: NewFlatFileInformationFork("testfile.txt"), - FlatFileDataForkHeader: FlatFileDataForkHeader{ + FlatFileDataForkHeader: FlatFileDataForkHeader{ ForkType: []byte("DATA"), CompressionType: []byte{0, 0, 0, 0}, RSVD: []byte{0, 0, 0, 0}, DataSize: []byte{0x00, 0x00, 0x00, 0x17}, }, - FileData: nil, + FileData: nil, }, - wantErr: false, + wantErr: assert.NoError, }, { name: "when file path is invalid", args: args{ - filePath: "./nope/", - fileName: "also-nope.txt", + fileRoot: func() string { path, _ := os.Getwd(); return path + "/test/config/Files" }(), + fileName: []byte("nope.txt"), }, - want: nil, - wantErr: true, + want: nil, + wantErr: assert.Error, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := NewFlattenedFileObject(tt.args.filePath, tt.args.fileName) - spew.Dump(got) - if (err != nil) != tt.wantErr { - t.Errorf("NewFlattenedFileObject() error = %v, wantErr %v", err, tt.wantErr) + got, err := NewFlattenedFileObject(tt.args.fileRoot, tt.args.filePath, tt.args.fileName) + if !tt.wantErr(t, err, fmt.Sprintf("NewFlattenedFileObject(%v, %v, %v)", tt.args.fileRoot, tt.args.filePath, tt.args.fileName)) { return } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("NewFlattenedFileObject() got = %v, want %v", got, tt.want) - } + assert.Equalf(t, tt.want, got, "NewFlattenedFileObject(%v, %v, %v)", tt.args.fileRoot, tt.args.filePath, tt.args.fileName) }) } -} \ No newline at end of file +} diff --git a/hotline/server.go b/hotline/server.go index 20ef49b..2ee31cf 100644 --- a/hotline/server.go +++ b/hotline/server.go @@ -636,11 +636,15 @@ func (s *Server) TransferFile(conn net.Conn) error { switch fileTransfer.Type { case FileDownload: - fullFilePath := fmt.Sprintf("%v/%v", s.Config.FileRoot+string(fileTransfer.FilePath), string(fileTransfer.FileName)) + fullFilePath, err := readPath(s.Config.FileRoot, fileTransfer.FilePath, fileTransfer.FileName) + if err != nil { + return err + } ffo, err := NewFlattenedFileObject( - s.Config.FileRoot+string(fileTransfer.FilePath), - string(fileTransfer.FileName), + s.Config.FileRoot, + fileTransfer.FilePath, + fileTransfer.FileName, ) if err != nil { return err @@ -653,7 +657,7 @@ func (s *Server) TransferFile(conn net.Conn) error { return err } - file, err := os.Open(fullFilePath) + file, err := FS.Open(fullFilePath) if err != nil { return err } @@ -756,9 +760,10 @@ func (s *Server) TransferFile(conn net.Conn) error { // // This notifies the server to send the next item header - var fh FilePath - _ = fh.UnmarshalBinary(fileTransfer.FilePath) - fullFilePath := fmt.Sprintf("%v/%v", s.Config.FileRoot+fh.String(), string(fileTransfer.FileName)) + fullFilePath, err := readPath(s.Config.FileRoot, fileTransfer.FilePath, fileTransfer.FileName) + if err != nil { + return err + } basePathLen := len(fullFilePath) @@ -798,7 +803,11 @@ func (s *Server) TransferFile(conn net.Conn) error { splitPath := strings.Split(path, "/") - ffo, err := NewFlattenedFileObject(strings.Join(splitPath[:len(splitPath)-1], "/"), info.Name()) + ffo, err := NewFlattenedFileObject( + strings.Join(splitPath[:len(splitPath)-1], "/"), + nil, + []byte(info.Name()), + ) if err != nil { return err } @@ -851,20 +860,23 @@ func (s *Server) TransferFile(conn net.Conn) error { }) case FolderUpload: - dstPath := s.Config.FileRoot + ReadFilePath(fileTransfer.FilePath) + "/" + string(fileTransfer.FileName) + dstPath, err := readPath(s.Config.FileRoot, fileTransfer.FilePath, fileTransfer.FileName) + if err != nil { + return err + } s.Logger.Infow( "Folder upload started", "transactionRef", fileTransfer.ReferenceNumber, "RemoteAddr", conn.RemoteAddr().String(), "dstPath", dstPath, - "TransferSize", fileTransfer.TransferSize, + "TransferSize", fmt.Sprintf("%x", fileTransfer.TransferSize), "FolderItemCount", fileTransfer.FolderItemCount, ) // Check if the target folder exists. If not, create it. - if _, err := os.Stat(dstPath); os.IsNotExist(err) { - s.Logger.Infow("Target path does not exist; Creating...", "dstPath", dstPath) - if err := os.Mkdir(dstPath, 0777); err != nil { + if _, err := FS.Stat(dstPath); os.IsNotExist(err) { + s.Logger.Infow("Creating target path", "dstPath", dstPath) + if err := FS.Mkdir(dstPath, 0777); err != nil { s.Logger.Error(err) } } diff --git a/hotline/server_blackbox_test.go b/hotline/server_blackbox_test.go index 64d56ab..601d745 100644 --- a/hotline/server_blackbox_test.go +++ b/hotline/server_blackbox_test.go @@ -66,6 +66,8 @@ func NewTestLogger() *zap.SugaredLogger { func StartTestServer() (*Server, context.Context, context.CancelFunc) { ctx, cancelRoot := context.WithCancel(context.Background()) + FS = OSFileStore{} + srv, err := NewServer("test/config/", "localhost", 0, NewTestLogger()) if err != nil { panic(err) @@ -82,6 +84,14 @@ func StartTestServer() (*Server, context.Context, context.CancelFunc) { } func TestHandshake(t *testing.T) { + mfs := MockFileStore{} + fh, _ := os.Open("./test/config/Agreement.txt") + mfs.On("Open", "/test/config/Agreement.txt").Return(fh, nil) + fh, _ = os.Open("./test/config/config.yaml") + mfs.On("Open", "/test/config/config.yaml").Return(fh, nil) + FS = mfs + spew.Dump(mfs) + srv, _, cancelFunc := StartTestServer() defer cancelFunc() @@ -313,18 +323,16 @@ func TestNewUser(t *testing.T) { func tranAssertEqual(t *testing.T, tran1, tran2 []Transaction) bool { var newT1 []Transaction var newT2 []Transaction - for _, trans := range tran1{ - trans.ID = []byte{0,0,0,0} + for _, trans := range tran1 { + trans.ID = []byte{0, 0, 0, 0} newT1 = append(newT1, trans) } - for _, trans := range tran2{ - trans.ID = []byte{0,0,0,0} + for _, trans := range tran2 { + trans.ID = []byte{0, 0, 0, 0} newT2 = append(newT2, trans) } - spew.Dump(newT1, newT2) - return assert.Equal(t, newT1, newT2) -} \ No newline at end of file +} diff --git a/hotline/transaction_handlers.go b/hotline/transaction_handlers.go index 9e61427..ea6d087 100644 --- a/hotline/transaction_handlers.go +++ b/hotline/transaction_handlers.go @@ -397,16 +397,16 @@ func HandleSendInstantMsg(cc *ClientConn, t *Transaction) (res []Transaction, er } func HandleGetFileInfo(cc *ClientConn, t *Transaction) (res []Transaction, err error) { - fileName := string(t.GetField(fieldFileName).Data) - filePath := cc.Server.Config.FileRoot + ReadFilePath(t.GetField(fieldFilePath).Data) + fileName := t.GetField(fieldFileName).Data + filePath := t.GetField(fieldFilePath).Data - ffo, err := NewFlattenedFileObject(filePath, fileName) + ffo, err := NewFlattenedFileObject(cc.Server.Config.FileRoot, filePath, fileName) if err != nil { return res, err } res = append(res, cc.NewReply(t, - NewField(fieldFileName, []byte(fileName)), + NewField(fieldFileName, fileName), NewField(fieldFileTypeString, ffo.FlatFileInformationFork.TypeSignature), NewField(fieldFileCreatorString, ffo.FlatFileInformationFork.CreatorSignature), NewField(fieldFileComment, ffo.FlatFileInformationFork.Comment), @@ -427,14 +427,24 @@ func HandleGetFileInfo(cc *ClientConn, t *Transaction) (res []Transaction, err e // * 210 File comment Optional // Fields used in the reply: None func HandleSetFileInfo(cc *ClientConn, t *Transaction) (res []Transaction, err error) { - fileName := string(t.GetField(fieldFileName).Data) - filePath := cc.Server.Config.FileRoot + ReadFilePath(t.GetField(fieldFilePath).Data) + fileName := t.GetField(fieldFileName).Data + filePath := t.GetField(fieldFilePath).Data + + fullFilePath, err := readPath(cc.Server.Config.FileRoot, filePath, fileName) + if err != nil { + return res, err + } + + fullNewFilePath, err := readPath(cc.Server.Config.FileRoot, filePath, t.GetField(fieldFileNewName).Data) + if err != nil { + return nil, err + } + //fileComment := t.GetField(fieldFileComment).Data fileNewName := t.GetField(fieldFileNewName).Data if fileNewName != nil { - path := filePath + "/" + fileName - fi, err := os.Stat(path) + fi, err := FS.Stat(fullFilePath) if err != nil { return res, err } @@ -451,9 +461,9 @@ func HandleSetFileInfo(cc *ClientConn, t *Transaction) (res []Transaction, err e } } - err = os.Rename(filePath+"/"+fileName, filePath+"/"+string(fileNewName)) + err = os.Rename(fullFilePath, fullNewFilePath) if os.IsNotExist(err) { - res = append(res, cc.NewErrReply(t, "Cannot rename file "+fileName+" because it does not exist or cannot be found.")) + res = append(res, cc.NewErrReply(t, "Cannot rename file "+string(fileName)+" because it does not exist or cannot be found.")) return res, err } } @@ -468,16 +478,19 @@ func HandleSetFileInfo(cc *ClientConn, t *Transaction) (res []Transaction, err e // * 202 File path // Fields used in the reply: none func HandleDeleteFile(cc *ClientConn, t *Transaction) (res []Transaction, err error) { - fileName := string(t.GetField(fieldFileName).Data) - filePath := cc.Server.Config.FileRoot + ReadFilePath(t.GetField(fieldFilePath).Data) + fileName := t.GetField(fieldFileName).Data + filePath := t.GetField(fieldFilePath).Data - path := filePath + fileName + fullFilePath, err := readPath(cc.Server.Config.FileRoot, filePath, fileName) + if err != nil { + return res, err + } - cc.Server.Logger.Debugw("Delete file", "src", path) + cc.Server.Logger.Debugw("Delete file", "src", fullFilePath) - fi, err := os.Stat(path) + fi, err := os.Stat(fullFilePath) if err != nil { - res = append(res, cc.NewErrReply(t, "Cannot delete file "+fileName+" because it does not exist or cannot be found.")) + res = append(res, cc.NewErrReply(t, "Cannot delete file "+string(fileName)+" because it does not exist or cannot be found.")) return res, nil } switch mode := fi.Mode(); { @@ -493,7 +506,7 @@ func HandleDeleteFile(cc *ClientConn, t *Transaction) (res []Transaction, err er } } - if err := os.RemoveAll(path); err != nil { + if err := os.RemoveAll(fullFilePath); err != nil { return res, err } @@ -1186,9 +1199,15 @@ func HandleGetMsgs(cc *ClientConn, t *Transaction) (res []Transaction, err error func HandleDownloadFile(cc *ClientConn, t *Transaction) (res []Transaction, err error) { fileName := t.GetField(fieldFileName).Data - filePath := ReadFilePath(t.GetField(fieldFilePath).Data) + filePath := t.GetField(fieldFilePath).Data - ffo, err := NewFlattenedFileObject(cc.Server.Config.FileRoot+filePath, string(fileName)) + var fp FilePath + err = fp.UnmarshalBinary(filePath) + if err != nil { + return res, err + } + + ffo, err := NewFlattenedFileObject(cc.Server.Config.FileRoot, filePath, fileName) if err != nil { return res, err } @@ -1196,11 +1215,9 @@ func HandleDownloadFile(cc *ClientConn, t *Transaction) (res []Transaction, err transactionRef := cc.Server.NewTransactionRef() data := binary.BigEndian.Uint32(transactionRef) - cc.Server.Logger.Infow("File download", "path", filePath) - ft := &FileTransfer{ FileName: fileName, - FilePath: []byte(filePath), + FilePath: filePath, ReferenceNumber: transactionRef, Type: FileDownload, } @@ -1261,7 +1278,8 @@ func HandleDownloadFolder(cc *ClientConn, t *Transaction) (res []Transaction, er return res, err } - fullFilePath := fmt.Sprintf("%v%v", cc.Server.Config.FileRoot+fp.String(), string(fileTransfer.FileName)) + fullFilePath, err := readPath(cc.Server.Config.FileRoot, t.GetField(fieldFilePath).Data, t.GetField(fieldFileName).Data) + transferSize, err := CalcTotalSize(fullFilePath) if err != nil { return res, err @@ -1390,14 +1408,16 @@ func HandleKeepAlive(cc *ClientConn, t *Transaction) (res []Transaction, err err } func HandleGetFileNameList(cc *ClientConn, t *Transaction) (res []Transaction, err error) { - filePath := cc.Server.Config.FileRoot - - path := t.GetField(fieldFilePath).Data - if len(path) > 0 { - filePath = cc.Server.Config.FileRoot + ReadFilePath(path) + fullPath, err := readPath( + cc.Server.Config.FileRoot, + t.GetField(fieldFilePath).Data, + nil, + ) + if err != nil { + return res, err } - fileNames, err := getFileNameList(filePath) + fileNames, err := getFileNameList(fullPath) if err != nil { return res, err } diff --git a/hotline/transaction_handlers_test.go b/hotline/transaction_handlers_test.go index ec947c9..f0a3ee7 100644 --- a/hotline/transaction_handlers_test.go +++ b/hotline/transaction_handlers_test.go @@ -469,7 +469,7 @@ func TestHandleGetFileInfo(t *testing.T) { ID: &[]byte{0x00, 0x01}, Server: &Server{ Config: &Config{ - FileRoot: "./test/config/Files/", + FileRoot: func() string { path, _ := os.Getwd(); return path + "/test/config/Files" }(), }, }, }, @@ -477,19 +477,6 @@ func TestHandleGetFileInfo(t *testing.T) { tranGetFileInfo, nil, NewField(fieldFileName, []byte("testfile.txt")), NewField(fieldFilePath, []byte{0x00, 0x00}), - //NewField(fieldFilePath, []byte{ - // 0x00, 0x03, - // 0x00, 0x00, - // 0x04, - // 0x74, 0x65, 0x73, 0x74, - // 0x00, 0x00, - // 0x06, - // 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - // - // 0x00, 0x00, - // 0x05, - // 0x46, 0x69, 0x6c, 0x65, 73}, - //), ), }, wantRes: []Transaction{ @@ -659,7 +646,6 @@ func TestHandleNewFolder(t *testing.T) { t: NewTransaction( tranNewFolder, &[]byte{0, 1}, NewField(fieldFileName, []byte("../../testFolder")), - ), }, setup: func() { @@ -677,7 +663,7 @@ func TestHandleNewFolder(t *testing.T) { ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1) ErrorCode: []byte{0, 0, 0, 0}, }, - }, wantErr: false, + }, wantErr: false, }, { name: "fieldFilePath does not allow directory traversal", @@ -719,7 +705,7 @@ func TestHandleNewFolder(t *testing.T) { ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1) ErrorCode: []byte{0, 0, 0, 0}, }, - }, wantErr: false, + }, wantErr: false, }, } for _, tt := range tests { @@ -738,3 +724,110 @@ func TestHandleNewFolder(t *testing.T) { } } +func TestHandleUploadFile(t *testing.T) { + type args struct { + cc *ClientConn + t *Transaction + } + tests := []struct { + name string + args args + wantRes []Transaction + wantErr bool + }{ + { + name: "when request is valid", + args: args{ + cc: &ClientConn{ + Server: &Server{ + FileTransfers: map[uint32]*FileTransfer{}, + }, + Account: &Account{ + Access: func() *[]byte { + var bits accessBitmap + bits.Set(accessUploadFile) + access := bits[:] + return &access + }(), + }, + }, + t: NewTransaction( + tranUploadFile, &[]byte{0, 1}, + NewField(fieldFileName, []byte("testFile")), + NewField(fieldFilePath, []byte{ + 0x00, 0x01, + 0x00, 0x00, + 0x03, + 0x2e, 0x2e, 0x2f, + }), + ), + }, + wantRes: []Transaction{ + { + Flags: 0x00, + IsReply: 0x01, + Type: []byte{0, 0xcb}, + ID: []byte{0x9a, 0xcb, 0x04, 0x42}, + ErrorCode: []byte{0, 0, 0, 0}, + Fields: []Field{ + NewField(fieldRefNum, []byte{0x52, 0xfd, 0xfc, 0x07}), // rand.Seed(1) + }, + }, + }, + wantErr: false, + }, + { + name: "when user does not have required access", + args: args{ + cc: &ClientConn{ + Account: &Account{ + Access: func() *[]byte { + var bits accessBitmap + access := bits[:] + return &access + }(), + }, + Server: &Server{ + FileTransfers: map[uint32]*FileTransfer{}, + }, + }, + t: NewTransaction( + tranUploadFile, &[]byte{0, 1}, + NewField(fieldFileName, []byte("testFile")), + NewField(fieldFilePath, []byte{ + 0x00, 0x01, + 0x00, 0x00, + 0x03, + 0x2e, 0x2e, 0x2f, + }), + ), + }, + wantRes: []Transaction{ + { + Flags: 0x00, + IsReply: 0x01, + Type: []byte{0, 0x00}, + ID: []byte{0x9a, 0xcb, 0x04, 0x42}, + ErrorCode: []byte{0, 0, 0, 1}, + Fields: []Field{ + NewField(fieldError, []byte("You are not allowed to upload files.")), // rand.Seed(1) + }, + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rand.Seed(1) + gotRes, err := HandleUploadFile(tt.args.cc, tt.args.t) + if (err != nil) != tt.wantErr { + t.Errorf("HandleUploadFile() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !tranAssertEqual(t, tt.wantRes, gotRes) { + t.Errorf("HandleUploadFile() gotRes = %v, want %v", gotRes, tt.wantRes) + } + }) + } +}