From e3cc56de0e3d72eab6eeff6a2ebbea631d59633c Mon Sep 17 00:00:00 2001 From: Sungkyu Yoo Date: Sun, 19 Apr 2026 22:33:11 +0900 Subject: [PATCH 1/2] Potential fix for code scanning alert no. 4: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- internal/services/lambda/store.go | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/internal/services/lambda/store.go b/internal/services/lambda/store.go index 1debbde9..5838edb9 100644 --- a/internal/services/lambda/store.go +++ b/internal/services/lambda/store.go @@ -147,10 +147,29 @@ func (s *LambdaStore) Close() error { // 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 strings.Contains(accountID, "/") || strings.Contains(accountID, "\\") || strings.Contains(accountID, "..") { + return "", fmt.Errorf("invalid account id path component") + } + if strings.Contains(functionName, "/") || strings.Contains(functionName, "\\") || strings.Contains(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) + + 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) + } + if !strings.HasPrefix(absCleaned, absBase+string(filepath.Separator)) { return "", fmt.Errorf("path traversal detected: %s", cleaned) } From f68855521488a20313add5af21a70e1c283cf7e3 Mon Sep 17 00:00:00 2001 From: Sung-Kyu Yoo Date: Mon, 20 Apr 2026 03:57:46 +0900 Subject: [PATCH 2/2] refactor: improve lambda path traversal validation Extract validPathComponent helper using strings.ContainsAny, switch containment check from HasPrefix to filepath.Rel for robustness, and allow equality (rel == "."). --- internal/services/lambda/store.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/internal/services/lambda/store.go b/internal/services/lambda/store.go index 5838edb9..ed4997f4 100644 --- a/internal/services/lambda/store.go +++ b/internal/services/lambda/store.go @@ -144,6 +144,12 @@ 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) { @@ -151,10 +157,10 @@ func (s *LambdaStore) codePath(accountID, functionName string) (string, error) { if accountID == "" || functionName == "" { return "", fmt.Errorf("invalid empty path component") } - if strings.Contains(accountID, "/") || strings.Contains(accountID, "\\") || strings.Contains(accountID, "..") { + if !validPathComponent(accountID) { return "", fmt.Errorf("invalid account id path component") } - if strings.Contains(functionName, "/") || strings.Contains(functionName, "\\") || strings.Contains(functionName, "..") { + if !validPathComponent(functionName) { return "", fmt.Errorf("invalid function name path component") } @@ -170,7 +176,11 @@ func (s *LambdaStore) codePath(accountID, functionName string) (string, error) { return "", fmt.Errorf("resolve code path: %w", err) } - if !strings.HasPrefix(absCleaned, absBase+string(filepath.Separator)) { + 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) } return cleaned, nil