Skip to content

Conversation

@darkoatanasovski
Copy link
Collaborator

No description provided.

cmd/main.go Outdated

fs.ServeHTTP(w, r)
// Check if exact file exists (e.g., /favicon.ico, /_next/static/...)
if info, err := os.Stat(resolvedPath); err == nil && !info.IsDir() {

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression

This path depends on a [user-provided value](1).

Copilot Autofix

AI 18 days ago

Copilot could not generate an autofix suggestion

Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.

cmd/main.go Outdated
fs.ServeHTTP(w, r)
// Check if exact file exists (e.g., /favicon.ico, /_next/static/...)
if info, err := os.Stat(resolvedPath); err == nil && !info.IsDir() {
http.ServeFile(w, r, resolvedPath)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression

This path depends on a [user-provided value](1).

Copilot Autofix

AI 18 days ago

Copilot could not generate an autofix suggestion

Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.

cmd/main.go Outdated
http.Error(w, "Invalid path", http.StatusBadRequest)
return
}
if info, err := os.Stat(htmlPathAbs); err == nil && !info.IsDir() {

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression

This path depends on a [user-provided value](1).

Copilot Autofix

AI 18 days ago

Copilot could not generate an autofix suggestion

Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.

cmd/main.go Outdated
return
}
if info, err := os.Stat(htmlPathAbs); err == nil && !info.IsDir() {
http.ServeFile(w, r, htmlPathAbs)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression

This path depends on a [user-provided value](1).

Copilot Autofix

AI 18 days ago

Copilot could not generate an autofix suggestion

Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.

cmd/main.go Outdated
http.Error(w, "Invalid path", http.StatusBadRequest)
return
}
if info, err := os.Stat(indexPathAbs); err == nil && !info.IsDir() {

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression

This path depends on a [user-provided value](1).

Copilot Autofix

AI 18 days ago

Copilot could not generate an autofix suggestion

Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.

cmd/main.go Outdated
return
}
if info, err := os.Stat(indexPathAbs); err == nil && !info.IsDir() {
http.ServeFile(w, r, indexPathAbs)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression

This path depends on a [user-provided value](1).

Copilot Autofix

AI 18 days ago

In general, the problem is that a file path derived from r.URL.Path is used in http.ServeFile after only a custom validation that the analyzer does not fully trust. The fix is to (a) make the path validation clearly and robustly constrain paths to live under a configured static root, and (b) ensure every path passed to http.ServeFile or other file operations is validated using that function. This addresses real traversal risk and also helps CodeQL recognize that tainted data is sanitized before reaching the sink.

The single best fix here, without changing functionality, is:

  1. Strengthen isPathWithinRoot to operate in a clearly “relative to root” manner by:

    • Cleaning the root path and turning it into an absolute path.
    • Cleaning the candidate path, joining it to the absolute root if it is not already absolute, and then resolving that to an absolute path.
    • Returning the resolved candidate only if it is equal to the root or has the root as a directory prefix (using absRoot + string(os.PathSeparator)).

    This keeps the same logical behavior (candidate must be within root) but makes the sanitizer clearer and less dependent on how the candidate was built earlier.

  2. Use isPathWithinRoot for the final index.html fallback as well, to maintain consistent protection and to make sure every served path is checked. Currently:

    • resolvedPath is validated.
    • htmlPathAbs and indexPathAbs are validated.
    • The final indexPath := filepath.Join(staticRoot, "index.html") is used directly in http.ServeFile without validation. While this particular path is constant and safe, reusing isPathWithinRoot there tightens the pattern and can help the analyzer understand that all ServeFile calls are guarded.

Concretely:

  • In cmd/main.go, update isPathWithinRoot (lines 23–36) to:

    • Clean and absolutize the root.
    • Clean the candidate, and if candidate is not absolute, join it with absRoot.
    • Absolutize the resulting candidate and perform the prefix check.
  • Near the bottom (lines 157–159), replace the direct join and ServeFile with:

    • Compute indexPath := filepath.Join(staticRoot, "index.html").
    • Pass it through isPathWithinRoot(staticRoot, indexPath) and handle the ok flag.
    • Use the validated indexPathAbs for http.ServeFile.

No new imports are needed; we reuse os, filepath, and strings that are already imported.

Suggested changeset 1
cmd/main.go

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/cmd/main.go b/cmd/main.go
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -19,17 +19,29 @@
 	"github.com/tikv/client-go/v2/rawkv"
 )
 
-// isPathWithinRoot returns true if candidate is the same as root or a descendant of it.
+// isPathWithinRoot returns the absolute candidate path and true if it is the same as root or a descendant of it.
 func isPathWithinRoot(root, candidate string) (string, bool) {
+	// Normalize and absolutize the root directory.
 	cleanRoot := filepath.Clean(root)
 	absRoot, err := filepath.Abs(cleanRoot)
 	if err != nil {
 		return "", false
 	}
-	absCandidate, err := filepath.Abs(candidate)
+
+	// Normalize the candidate path.
+	cleanCandidate := filepath.Clean(candidate)
+
+	// If candidate is not absolute, interpret it as relative to absRoot.
+	if !filepath.IsAbs(cleanCandidate) {
+		cleanCandidate = filepath.Join(absRoot, cleanCandidate)
+	}
+
+	absCandidate, err := filepath.Abs(cleanCandidate)
 	if err != nil {
 		return "", false
 	}
+
+	// Candidate must be equal to root or reside within it.
 	if absCandidate == absRoot || strings.HasPrefix(absCandidate, absRoot+string(os.PathSeparator)) {
 		return absCandidate, true
 	}
@@ -156,7 +159,12 @@
 
 		// Default: serve index.html (home page or SPA fallback)
 		indexPath := filepath.Join(staticRoot, "index.html")
-		http.ServeFile(w, r, indexPath)
+		indexPathAbs, ok := isPathWithinRoot(staticRoot, indexPath)
+		if !ok {
+			http.Error(w, "Invalid path", http.StatusBadRequest)
+			return
+		}
+		http.ServeFile(w, r, indexPathAbs)
 	})
 	port := os.Getenv("PORT")
 	if port == "" {
EOF
@@ -19,17 +19,29 @@
"github.com/tikv/client-go/v2/rawkv"
)

// isPathWithinRoot returns true if candidate is the same as root or a descendant of it.
// isPathWithinRoot returns the absolute candidate path and true if it is the same as root or a descendant of it.
func isPathWithinRoot(root, candidate string) (string, bool) {
// Normalize and absolutize the root directory.
cleanRoot := filepath.Clean(root)
absRoot, err := filepath.Abs(cleanRoot)
if err != nil {
return "", false
}
absCandidate, err := filepath.Abs(candidate)

// Normalize the candidate path.
cleanCandidate := filepath.Clean(candidate)

// If candidate is not absolute, interpret it as relative to absRoot.
if !filepath.IsAbs(cleanCandidate) {
cleanCandidate = filepath.Join(absRoot, cleanCandidate)
}

absCandidate, err := filepath.Abs(cleanCandidate)
if err != nil {
return "", false
}

// Candidate must be equal to root or reside within it.
if absCandidate == absRoot || strings.HasPrefix(absCandidate, absRoot+string(os.PathSeparator)) {
return absCandidate, true
}
@@ -156,7 +159,12 @@

// Default: serve index.html (home page or SPA fallback)
indexPath := filepath.Join(staticRoot, "index.html")
http.ServeFile(w, r, indexPath)
indexPathAbs, ok := isPathWithinRoot(staticRoot, indexPath)
if !ok {
http.Error(w, "Invalid path", http.StatusBadRequest)
return
}
http.ServeFile(w, r, indexPathAbs)
})
port := os.Getenv("PORT")
if port == "" {
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
@darkoatanasovski darkoatanasovski merged commit 1cf60da into master Dec 21, 2025
7 of 8 checks passed
@darkoatanasovski darkoatanasovski deleted the improve-workflows branch December 21, 2025 21:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants