Skip to content

Commit c5c552e

Browse files
committed
Refactor file paths in JamfPro and MS Graph API request files to use filepath.Base() for secure file handling
1 parent 8388615 commit c5c552e

File tree

3 files changed

+43
-14
lines changed

3 files changed

+43
-14
lines changed

apiintegrations/jamfpro/jamfpro_api_request.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ import (
77
"encoding/xml"
88
"io"
99
"mime/multipart"
10-
"os"
10+
"path/filepath"
1111
"strings"
1212

13+
"github.com/deploymenttheory/go-api-http-client/helpers"
1314
"github.com/deploymenttheory/go-api-http-client/logger"
1415
"go.uber.org/zap"
1516
)
@@ -55,7 +56,7 @@ func (j *JamfAPIHandler) MarshalRequest(body interface{}, method string, endpoin
5556
return data, nil
5657
}
5758

58-
// MarshalMultipartFormData takes a map with form fields and file paths and returns the encoded body and content type.
59+
// MarshalMultipartRequest handles multipart form data encoding with secure file handling and returns the encoded body and content type.
5960
func (j *JamfAPIHandler) MarshalMultipartRequest(fields map[string]string, files map[string]string, log logger.Logger) ([]byte, string, error) {
6061
body := &bytes.Buffer{}
6162
writer := multipart.NewWriter(body)
@@ -67,15 +68,16 @@ func (j *JamfAPIHandler) MarshalMultipartRequest(fields map[string]string, files
6768
}
6869
}
6970

70-
// Add the files to the form data
71-
for formField, filepath := range files {
72-
file, err := os.Open(filepath)
71+
// Add the files to the form data, using safeOpenFile to ensure secure file access
72+
for formField, filePath := range files {
73+
file, err := helpers.SafeOpenFile(filePath)
7374
if err != nil {
75+
log.Error("Failed to open file securely", zap.String("file", filePath), zap.Error(err))
7476
return nil, "", err
7577
}
7678
defer file.Close()
7779

78-
part, err := writer.CreateFormFile(formField, filepath)
80+
part, err := writer.CreateFormFile(formField, filepath.Base(filePath))
7981
if err != nil {
8082
return nil, "", err
8183
}
@@ -84,7 +86,7 @@ func (j *JamfAPIHandler) MarshalMultipartRequest(fields map[string]string, files
8486
}
8587
}
8688

87-
// Close the writer before returning
89+
// Close the writer to finish writing the multipart message
8890
contentType := writer.FormDataContentType()
8991
if err := writer.Close(); err != nil {
9092
return nil, "", err

apiintegrations/msgraph/msgraph_api_request.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import (
66
"encoding/json"
77
"io"
88
"mime/multipart"
9-
"os"
9+
"path/filepath"
1010

11+
"github.com/deploymenttheory/go-api-http-client/helpers"
1112
"github.com/deploymenttheory/go-api-http-client/logger"
1213
"go.uber.org/zap"
1314
)
@@ -29,8 +30,9 @@ func (g *GraphAPIHandler) MarshalRequest(body interface{}, method string, endpoi
2930
return data, nil
3031
}
3132

32-
// MarshalMultipartFormData takes a map with form fields and file paths and returns the encoded body and content type.
33+
// MarshalMultipartRequest handles multipart form data encoding with secure file handling and returns the encoded body and content type.
3334
func (g *GraphAPIHandler) MarshalMultipartRequest(fields map[string]string, files map[string]string, log logger.Logger) ([]byte, string, error) {
35+
3436
body := &bytes.Buffer{}
3537
writer := multipart.NewWriter(body)
3638

@@ -41,15 +43,16 @@ func (g *GraphAPIHandler) MarshalMultipartRequest(fields map[string]string, file
4143
}
4244
}
4345

44-
// Add the files to the form data
45-
for formField, filepath := range files {
46-
file, err := os.Open(filepath)
46+
// Add the files to the form data, using safeOpenFile to ensure secure file access
47+
for formField, filePath := range files {
48+
file, err := helpers.SafeOpenFile(filePath)
4749
if err != nil {
50+
log.Error("Failed to open file securely", zap.String("file", filePath), zap.Error(err))
4851
return nil, "", err
4952
}
5053
defer file.Close()
5154

52-
part, err := writer.CreateFormFile(formField, filepath)
55+
part, err := writer.CreateFormFile(formField, filepath.Base(filePath))
5356
if err != nil {
5457
return nil, "", err
5558
}
@@ -58,7 +61,7 @@ func (g *GraphAPIHandler) MarshalMultipartRequest(fields map[string]string, file
5861
}
5962
}
6063

61-
// Close the writer before returning
64+
// Close the writer to finish writing the multipart message
6265
contentType := writer.FormDataContentType()
6366
if err := writer.Close(); err != nil {
6467
return nil, "", err

helpers/helpers.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,34 @@
22
package helpers
33

44
import (
5+
"fmt"
6+
"os"
7+
"path/filepath"
58
"time"
69
)
710

811
// ParseISO8601Date attempts to parse a string date in ISO 8601 format.
912
func ParseISO8601Date(dateStr string) (time.Time, error) {
1013
return time.Parse(time.RFC3339, dateStr)
1114
}
15+
16+
// SafeOpenFile opens a file safely after validating and resolving its path.
17+
func SafeOpenFile(filePath string) (*os.File, error) {
18+
// Clean the file path to remove any ".." or similar components that can lead to directory traversal
19+
cleanPath := filepath.Clean(filePath)
20+
21+
// Resolve the clean path to an absolute path and ensure it resolves any symbolic links
22+
absPath, err := filepath.EvalSymlinks(cleanPath)
23+
if err != nil {
24+
return nil, fmt.Errorf("unable to resolve the absolute path: %s, error: %w", filePath, err)
25+
}
26+
27+
// Optionally, check if the absolute path is within a permitted directory (omitted here for brevity)
28+
// Example: allowedPathPrefix := "/safe/directory/"
29+
// if !strings.HasPrefix(absPath, allowedPathPrefix) {
30+
// return nil, fmt.Errorf("access to the file path is not allowed: %s", absPath)
31+
// }
32+
33+
// Open the file if the path is deemed safe
34+
return os.Open(absPath)
35+
}

0 commit comments

Comments
 (0)