Skip to content

Commit 57a396a

Browse files
committed
fix: prevent path traversal in file delivery and fix directory navigation in Dockerfile build process
1 parent 3818016 commit 57a396a

2 files changed

Lines changed: 25 additions & 11 deletions

File tree

Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
1616
RUN git clone --depth 1 https://github.com/trufflesecurity/trufflehog.git /tmp/trufflehog && \
1717
cd /tmp/trufflehog && \
1818
go build -o /go/bin/trufflehog . && \
19+
cd /app && \
1920
rm -rf /tmp/trufflehog && \
2021
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest && \
2122
go install -v github.com/jaeles-project/gospider@latest && \

internal/api/api.go

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1967,42 +1967,55 @@ func sendFileToDiscord(c *gin.Context) {
19671967
utils.GetLogger().Infof("[API] [sendFileToDiscord] Sending to thread %s (instead of channel %s)", threadID, channelID)
19681968
}
19691969

1970+
// Prevent Arbitrary File Read (Path Traversal)
1971+
cleanPath, err := filepath.Abs(req.FilePath)
1972+
if err != nil {
1973+
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid file path"})
1974+
return
1975+
}
1976+
resultsDir, _ := filepath.Abs(utils.GetResultsDir())
1977+
if !strings.HasPrefix(cleanPath, resultsDir) {
1978+
utils.GetLogger().Warnf("[API] [sendFileToDiscord] [SECURITY] Attempted to read file outside results directory: %s", cleanPath)
1979+
c.JSON(http.StatusForbidden, gin.H{"error": "file must be located within the results directory"})
1980+
return
1981+
}
1982+
19701983
// Check if file exists
1971-
utils.GetLogger().Infof("[API] [sendFileToDiscord] Checking if file exists: %s", req.FilePath)
1972-
if info, err := os.Stat(req.FilePath); os.IsNotExist(err) {
1973-
utils.GetLogger().Infof("[API] [sendFileToDiscord] [ERROR] File not found: %s", req.FilePath)
1984+
utils.GetLogger().Infof("[API] [sendFileToDiscord] Checking if file exists: %s", cleanPath)
1985+
if info, err := os.Stat(cleanPath); os.IsNotExist(err) {
1986+
utils.GetLogger().Infof("[API] [sendFileToDiscord] [ERROR] File not found: %s", cleanPath)
19741987
c.JSON(http.StatusNotFound, gin.H{"error": "file not found"})
19751988
return
19761989
} else if err != nil {
19771990
utils.GetLogger().Infof("[API] [sendFileToDiscord] [ERROR] Failed to stat file: %v", err)
19781991
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("failed to stat file: %v", err)})
19791992
return
19801993
} else {
1981-
utils.GetLogger().Infof("[API] [sendFileToDiscord] File found: %s (size: %d bytes)", req.FilePath, info.Size())
1994+
utils.GetLogger().Infof("[API] [sendFileToDiscord] File found: %s (size: %d bytes)", cleanPath, info.Size())
19821995
}
19831996

19841997
// Get file info
1985-
fileName := filepath.Base(req.FilePath)
1998+
fileName := filepath.Base(cleanPath)
19861999
description := req.Description
19872000
if description == "" {
19882001
description = fmt.Sprintf("📁 %s", fileName)
19892002
}
19902003

19912004
// Get file info for size check
1992-
fileInfo, err := os.Stat(req.FilePath)
2005+
fileInfo, err := os.Stat(cleanPath)
19932006
if err != nil {
19942007
utils.GetLogger().Infof("[API] [sendFileToDiscord] [ERROR] Failed to stat file: %v", err)
19952008
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("failed to stat file: %v", err)})
19962009
return
19972010
}
19982011

19992012
// Check if file should use R2
2000-
useR2 := r2storage.ShouldUseR2(req.FilePath) || (r2storage.IsEnabled() && fileInfo.Size() > r2storage.GetFileSizeLimit())
2013+
useR2 := r2storage.ShouldUseR2(cleanPath) || (r2storage.IsEnabled() && fileInfo.Size() > r2storage.GetFileSizeLimit())
20012014

20022015
if useR2 {
20032016
// Upload to R2 and send link
20042017
utils.GetLogger().Infof("[API] [sendFileToDiscord] File is large (%d bytes), uploading to R2...", fileInfo.Size())
2005-
publicURL, err := r2storage.UploadFile(req.FilePath, fileName, false)
2018+
publicURL, err := r2storage.UploadFile(cleanPath, fileName, false)
20062019
if err != nil {
20072020
utils.GetLogger().Infof("[API] [sendFileToDiscord] [ERROR] Failed to upload to R2: %v", err)
20082021
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("failed to upload to R2: %v", err)})
@@ -2039,8 +2052,8 @@ func sendFileToDiscord(c *gin.Context) {
20392052
}
20402053

20412054
// #4: Stream file via os.Open instead of loading everything into RAM with os.ReadFile.
2042-
utils.GetLogger().Infof("[API] [sendFileToDiscord] Opening file for streaming: %s", req.FilePath)
2043-
fileStream, streamErr := os.Open(req.FilePath)
2055+
utils.GetLogger().Infof("[API] [sendFileToDiscord] Opening file for streaming: %s", cleanPath)
2056+
fileStream, streamErr := os.Open(cleanPath)
20442057
if streamErr != nil {
20452058
utils.GetLogger().Infof("[API] [sendFileToDiscord] [ERROR] Failed to open file: %v", streamErr)
20462059
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("failed to open file: %v", streamErr)})
@@ -2087,7 +2100,7 @@ func sendFileToDiscord(c *gin.Context) {
20872100
if strings.Contains(err.Error(), "413") || strings.Contains(err.Error(), "too large") || strings.Contains(err.Error(), "Request entity too large") {
20882101
utils.GetLogger().Infof("[API] [sendFileToDiscord] ⚠️ Discord upload failed due to size, uploading to R2 as fallback...")
20892102
if r2storage.IsEnabled() {
2090-
publicURL, r2Err := r2storage.UploadFile(req.FilePath, fileName, false)
2103+
publicURL, r2Err := r2storage.UploadFile(cleanPath, fileName, false)
20912104
if r2Err != nil {
20922105
utils.GetLogger().Infof("[API] [sendFileToDiscord] [ERROR] Failed to upload to R2: %v", r2Err)
20932106
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("failed to send file and R2 upload failed: %v (R2 error: %v)", err, r2Err)})

0 commit comments

Comments
 (0)