From 05f0cf23059e931b85a35306723e63d30a241da2 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Sun, 25 Jan 2026 12:12:35 +1100 Subject: [PATCH] Potential fix for code scanning alert no. 172: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- v3/examples/screen/main.go | 39 +++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/v3/examples/screen/main.go b/v3/examples/screen/main.go index 33b4c1f34..3f4981d5b 100644 --- a/v3/examples/screen/main.go +++ b/v3/examples/screen/main.go @@ -47,22 +47,39 @@ func main() { dir := filepath.Dir(filename) assetsDir := filepath.Join(dir, "assets") - // Clean and validate the path to prevent directory traversal - cleanPath := filepath.Clean(r.URL.Path) - fullPath := filepath.Join(assetsDir, cleanPath) - - // Ensure the resolved path is still within the assets directory. - // This check prevents path traversal attacks like "/../../../etc/passwd" - if !strings.HasPrefix(fullPath, assetsDir+string(filepath.Separator)) && fullPath != assetsDir { - // Path traversal attempt detected, fall back to default handler + // Resolve the assets directory to an absolute, cleaned path. + assetsDirAbs, err := filepath.Abs(filepath.Clean(assetsDir)) + if err != nil { + // If we cannot resolve the assets directory safely, fall back to default handler. next.ServeHTTP(w, r) return } - // Path is validated to be within assetsDir above (lines 55-60) - if _, err := os.Stat(fullPath); err == nil { // #nosec G304 // lgtm[go/path-injection] -- path validated above + // Clean the requested URL path and make it relative, to prevent directory traversal + cleanPath := filepath.Clean(r.URL.Path) + // Treat the request path as relative by stripping any leading slash. + relativePath := strings.TrimPrefix(cleanPath, string(filepath.Separator)) + + // Resolve the requested path against the absolute assets directory. + resolvedPath, err := filepath.Abs(filepath.Join(assetsDirAbs, relativePath)) + if err != nil { + // If the path cannot be resolved, fall back to default handler. + next.ServeHTTP(w, r) + return + } + + // Ensure the resolved path is still within the assets directory. + // This check prevents path traversal attacks like "/../../../etc/passwd". + if resolvedPath != assetsDirAbs && !strings.HasPrefix(resolvedPath, assetsDirAbs+string(filepath.Separator)) { + // Path traversal attempt detected, fall back to default handler. + next.ServeHTTP(w, r) + return + } + + // Path is validated to be within assetsDirAbs above (lines 71-77). + if _, err := os.Stat(resolvedPath); err == nil { // #nosec G304 // lgtm[go/path-injection] -- path validated above // Serve file from disk to make testing easy - http.ServeFile(w, r, fullPath) // #nosec G304 // lgtm[go/path-injection] -- path validated above + http.ServeFile(w, r, resolvedPath) // #nosec G304 // lgtm[go/path-injection] -- path validated above } else { // Passthrough to the default asset handler if file not found on disk next.ServeHTTP(w, r)