Potential fix for code scanning alert no. 1: Uncontrolled data used in path expression#17
Potential fix for code scanning alert no. 1: Uncontrolled data used in path expression#17
Conversation
…n path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
| return | ||
| } | ||
|
|
||
| if _, err := os.Stat(requestedAbs); os.IsNotExist(err) { |
Check failure
Code scanning / CodeQL
Uncontrolled data used in path expression High
This autofix suggestion was applied.
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 5 days ago
To fix this, keep using absolute, normalized paths but strengthen the containment check between requestedAbs and staticAbs in a clear, standard way. Specifically, use strings.HasPrefix and ensure that either requestedAbs is exactly staticAbs or that it starts with staticAbs followed by the OS-specific path separator. This guarantees that /var/www/static2 is not treated as contained in /var/www/static, and it makes the intent explicit. Additionally, use filepath.Join instead of concatenating h.staticDir + "/index.html" so that path construction is correct and OS-independent.
Concretely, in backend/cmd/main.go:
- Import the standard library package
strings. - In
spaHandler.ServeHTTP:- Compute
indexFile := filepath.Join(staticAbs, "index.html")(after you knowstaticAbs), and reuse it for all fallback calls tohttp.ServeFile. - Replace the manual prefix check
len(requestedAbs) < len(staticAbs) || requestedAbs[:len(staticAbs)] != staticAbswith a more robust boundary-aware prefix check usingstrings.HasPrefixandstring(os.PathSeparator).
- Compute
This preserves existing behavior (serving static files when inside the designated directory and index.html otherwise) while removing the path traversal risk and aligning with common secure path-handling patterns.
| @@ -17,6 +17,7 @@ | ||
| "os" | ||
| "os/signal" | ||
| "path/filepath" | ||
| "strings" | ||
| "syscall" | ||
| "time" | ||
|
|
||
| @@ -130,27 +131,29 @@ | ||
| staticAbs, err := filepath.Abs(h.staticDir) | ||
| if err != nil { | ||
| // If we cannot resolve the static directory, fall back to the SPA shell. | ||
| http.ServeFile(w, r, h.staticDir+"/index.html") | ||
| http.ServeFile(w, r, filepath.Join(h.staticDir, "index.html")) | ||
| return | ||
| } | ||
|
|
||
| indexFile := filepath.Join(staticAbs, "index.html") | ||
|
|
||
| requestedPath := filepath.Join(staticAbs, r.URL.Path) | ||
| requestedAbs, err := filepath.Abs(requestedPath) | ||
| if err != nil { | ||
| // On error resolving the requested path, fall back to the SPA shell. | ||
| http.ServeFile(w, r, h.staticDir+"/index.html") | ||
| http.ServeFile(w, r, indexFile) | ||
| return | ||
| } | ||
|
|
||
| // Ensure the requested path is within the static directory to prevent directory traversal. | ||
| if len(requestedAbs) < len(staticAbs) || requestedAbs[:len(staticAbs)] != staticAbs { | ||
| http.ServeFile(w, r, h.staticDir+"/index.html") | ||
| if !(requestedAbs == staticAbs || strings.HasPrefix(requestedAbs, staticAbs+string(os.PathSeparator))) { | ||
| http.ServeFile(w, r, indexFile) | ||
| return | ||
| } | ||
|
|
||
| if _, err := os.Stat(requestedAbs); os.IsNotExist(err) { | ||
| // Not a real file – serve the SPA shell | ||
| http.ServeFile(w, r, h.staticDir+"/index.html") | ||
| http.ServeFile(w, r, indexFile) | ||
| return | ||
| } | ||
|
|
…n path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Potential fix for https://github.com/Bajahaw/ai-ui/security/code-scanning/1
In general, the fix is to ensure that any filesystem path derived from
r.URL.Pathis constrained to a known safe directory. That typically means joiningr.URL.Pathtoh.staticDir, resolving it to an absolute path, and confirming that the result is still underh.staticDirbefore using it. If the check fails, the handler should return an error (or the SPA shell) instead of touching the filesystem.For this specific code, the simplest, non‑functional change is to: (1) derive the candidate path using
filepath.Join(h.staticDir, r.URL.Path), (2) normalize it withfilepath.Abs, (3) ensure it still lives underh.staticDir(using a normalized absolute version ofh.staticDir), and (4) use that safe, normalized path foros.Stat. If the path escapes the static directory or normalization fails, we treat it as “not a real file” and fall back toindex.html(the existing behavior for non‑existent files). This avoids leaking whether arbitrary files outsideh.staticDirexist, and keeps the rest of the behavior intact. To implement this, we need to import"path/filepath"and computestaticAbsonce per request before the check.Concretely:
path/filepathto the imports inbackend/cmd/main.go.spaHandler.ServeHTTP, replace thepath := "." + r.URL.Pathand directos.Stat(h.staticDir + "/" + r.URL.Path)with logic that:staticAbs, err := filepath.Abs(h.staticDir)andrequested, err := filepath.Abs(filepath.Join(staticAbs, r.URL.Path)).requestedis not understaticAbs, treat it as not a real file and serveindex.html.os.Stat(requested)to decide whether to serve the SPA shell or leth.fshandle the static file.pathvariable can be removed.Suggested fixes powered by Copilot Autofix. Review carefully before merging.