Skip to content
Merged
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
35 changes: 32 additions & 3 deletions internal/services/lambda/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,14 +144,43 @@ func (s *LambdaStore) Close() error {
return s.store.Close()
}

// validPathComponent returns true if s is a single path component with no
// separators or traversal sequences.
func validPathComponent(s string) bool {
return !strings.ContainsAny(s, "/\\") && !strings.Contains(s, "..")
}

// codePath returns the filesystem path for a function's code zip.
// It validates the result stays under codeDir to prevent path traversal.
func (s *LambdaStore) codePath(accountID, functionName string) (string, error) {
// accountID and functionName are expected to be single path components.
if accountID == "" || functionName == "" {
return "", fmt.Errorf("invalid empty path component")
}
if !validPathComponent(accountID) {
return "", fmt.Errorf("invalid account id path component")
}
if !validPathComponent(functionName) {
return "", fmt.Errorf("invalid function name path component")
}

joined := filepath.Join(s.codeDir, accountID, functionName, "code.zip")
cleaned := filepath.Clean(joined)
absBase, _ := filepath.Abs(s.codeDir)
absCleaned, _ := filepath.Abs(cleaned)
if !strings.HasPrefix(absCleaned, absBase+string(filepath.Separator)) {

absBase, err := filepath.Abs(s.codeDir)
if err != nil {
return "", fmt.Errorf("resolve base code directory: %w", err)
}
absCleaned, err := filepath.Abs(cleaned)
if err != nil {
return "", fmt.Errorf("resolve code path: %w", err)
}

rel, err := filepath.Rel(absBase, absCleaned)
if err != nil {
return "", fmt.Errorf("resolve relative code path: %w", err)
}
if rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
return "", fmt.Errorf("path traversal detected: %s", cleaned)
}
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
return cleaned, nil
Expand Down
Loading