Skip to content

Potential fix for code scanning alert no. 1: Uncontrolled data used in path expression#2

Merged
inhere merged 1 commit into
mainfrom
alert-autofix-1
Apr 27, 2026
Merged

Potential fix for code scanning alert no. 1: Uncontrolled data used in path expression#2
inhere merged 1 commit into
mainfrom
alert-autofix-1

Conversation

@inhere

@inhere inhere commented Apr 27, 2026

Copy link
Copy Markdown
Owner

Potential fix for https://github.com/inhere/skillc/security/code-scanning/1

Use a canonical “resolve-and-verify-within-root” check before os.ReadFile:

  • Resolve skillDir to an absolute, symlink-evaluated root (filepath.Abs + filepath.EvalSymlinks).
  • Resolve the requested file similarly after join/clean.
  • Verify the resolved target is exactly the root or strictly under it using filepath.Rel (reject if rel == ".." or starts with ".."+separator).
  • Only then read the file.

This preserves existing functionality (serving files under the skill directory) while making the validation robust against symlink and normalization bypasses.
Edits are only in internal/app/webapp/server.go, inside the /file handler around current lines 97–108. No new imports are needed.

Suggested fixes powered by Copilot Autofix. Review carefully before merging.

…n path expression

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
@inhere inhere marked this pull request as ready for review April 27, 2026 06:25
Copilot AI review requested due to automatic review settings April 27, 2026 06:25

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses code scanning alert #1 (“Uncontrolled data used in path expression”) in the webapp /file endpoint by strengthening path traversal protections when reading files under a skill directory.

Changes:

  • Canonicalizes the skill root directory using filepath.Abs + filepath.EvalSymlinks.
  • Canonicalizes the requested file path and verifies it resolves within the canonical root using filepath.Rel before calling os.ReadFile.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +122 to +127
abs, err := filepath.EvalSymlinks(candidateAbs)
if err != nil {
resp.Error = "invalid path"
w.WriteHeader(http.StatusBadRequest)
_ = json.NewEncoder(w).Encode(resp)
return

Copilot AI Apr 27, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filepath.EvalSymlinks(candidateAbs) will fail with os.IsNotExist for legitimate requests to files that simply don’t exist, causing a 400 "invalid path" instead of the prior 404 from os.ReadFile. If the intent is to preserve existing behavior, consider treating os.IsNotExist(err) differently (e.g., perform the within-root check on candidateAbs and return 404 / let ReadFile handle it), while still using EvalSymlinks + re-check for existing paths to block symlink escapes.

Copilot uses AI. Check for mistakes.
Comment on lines +99 to +107
rootAbs, err := filepath.Abs(skillDir)
if err != nil {
resp.Error = "invalid path"
w.WriteHeader(http.StatusBadRequest)
_ = json.NewEncoder(w).Encode(resp)
return
}
rootEval, err := filepath.EvalSymlinks(rootAbs)
if err != nil {

Copilot AI Apr 27, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rootAbs/rootEval are derived solely from skillDir (constant for the lifetime of this server) but are recomputed on every /file request. Consider resolving/validating the root once before registering the handler (and failing Serve early if resolution fails), then closing over the resolved root to reduce per-request overhead and simplify the handler.

Copilot uses AI. Check for mistakes.
Comment on lines +100 to +105
if err != nil {
resp.Error = "invalid path"
w.WriteHeader(http.StatusBadRequest)
_ = json.NewEncoder(w).Encode(resp)
return
}

Copilot AI Apr 27, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The handler repeats the same 5-line "set error + status + encode JSON + return" block for each failure case. Consider extracting a small helper (e.g., writeJSONError(w, status, msg)) to keep the path-validation logic readable and reduce the chance of inconsistent headers/status codes when this code is modified later.

Copilot uses AI. Check for mistakes.
@inhere inhere merged commit d3ce96f into main Apr 27, 2026
13 checks passed
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