]> git.r.bdr.sh - rbdr/mobius/commitdiff
Sanitize file path input to prevent directory traversal
authorJeff Halter <redacted>
Mon, 31 Jan 2022 04:56:04 +0000 (20:56 -0800)
committerJeff Halter <redacted>
Mon, 31 Jan 2022 04:56:04 +0000 (20:56 -0800)
hotline/file_path.go
hotline/file_path_test.go
hotline/flattened_file_object.go
hotline/flattened_file_object_test.go
hotline/server.go
hotline/server_blackbox_test.go
hotline/transaction_handlers.go
hotline/transaction_handlers_test.go

index d6c05bcbcfb581d4df6d7f0dd89e2eb0670cea81..2e2e085046a6269f5f577930ec7b7e1781d2c436 100644 (file)
@@ -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
+}
index a8ab2ce29c3bdee557f54389e2f985958ed2ad8c..4c2f6db80c855e1741595ab5e1210b358afda4b4 100644 (file)
@@ -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
index 656cdd37b87ba01a8df3dfb62b10a3897c6ad180..02804f697f44e62d89cf4f039330672af5ef35f7 100644 (file)
@@ -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},
index 19b7c94afbbb22da78f7b7d5918a0e511cbea5cc..df05c09f37b41a98db07b94399d72ac6ed825eca 100644 (file)
@@ -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
+}
index 20ef49b9a082bdbc73f91e1462392e96c1651858..2ee31cf610627dc66956d04f1a1f74852e9315f8 100644 (file)
@@ -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)
                        }
                }
index 64d56ab97391341a9023e1a22ed0ecaac30cfc8b..601d745e40a215a15c8ea99567b07d0ea6f0ddb4 100644 (file)
@@ -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
+}
index 9e61427d4198f5099a379ee3e2ac42732452e30d..ea6d087540ec9a03e9fc3071810772f6888dfcf8 100644 (file)
@@ -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
        }
index ec947c9feeb2ded12de6fb71c4309b2febdaf93d..f0a3ee746f39bb74fe9c4fa4e3a5aecfb1594125 100644 (file)
@@ -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)
+                       }
+               })
+       }
+}