Skip to content

Prevent path traversal in LocalStore.getFilePath#5464

Open
immanuwell wants to merge 1 commit into
stacklok:mainfrom
immanuwell:fix/localstore-path-traversal
Open

Prevent path traversal in LocalStore.getFilePath#5464
immanuwell wants to merge 1 commit into
stacklok:mainfrom
immanuwell:fix/localstore-path-traversal

Conversation

@immanuwell
Copy link
Copy Markdown
Contributor

@immanuwell immanuwell commented Jun 6, 2026

Summary

  • LocalStore.getFilePath used filepath.Join to build file paths but never verified the result stayed within basePath. A name like ../../../etc/passwd would escape the state directory, making GetReader, GetWriter, CreateExclusive, Delete, and Exists all vulnerable to path traversal
  • Add a containment check after filepath.Join (which calls filepath.Clean to resolve .. components) using the strings.HasPrefix(path, basePath+separator) pattern already established in pkg/fileutils/contained.go
  • Change getFilePath to return (string, error) so callers can propagate the error
  • Update the #nosec G304 comments to accurately reflect that containment is now actually enforced
  • Add local_test.go with tests covering path traversal rejection across all five affected methods, normal operations, and a check that written files stay inside basePath

Fixes #4736

Type of change

  • Bug fix

Test plan

  • Unit tests (task test)
  • Linting (task lint-fix)

New test TestLocalStore_PathTraversalPrevented exercises five traversal patterns (../escape, ../../etc/passwd, ../../../root/.ssh/authorized_keys, ./../escape, subdir/../../escape) against all five public methods and asserts each returns a "path traversal detected" error.

getFilePath constructed file paths with filepath.Join but never
verified the result stayed within basePath. A name like
"../../../etc/passwd" would resolve outside the state directory,
making GetReader, GetWriter, CreateExclusive, Delete, and Exists
all vulnerable to path traversal.

Fix: after filepath.Join (which calls filepath.Clean to resolve ".."
components), check that the result starts with basePath+separator
using the same containment pattern already established in
pkg/fileutils/contained.go. Update the #nosec G304 comments to
reflect that the check is now actually enforced.

Closes stacklok#4736
@github-actions github-actions Bot added the size/S Small PR: 100-299 lines changed label Jun 6, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 6, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 69.14%. Comparing base (4a9af53) to head (af23782).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5464      +/-   ##
==========================================
- Coverage   69.17%   69.14%   -0.03%     
==========================================
  Files         634      634              
  Lines       64473    64486      +13     
==========================================
- Hits        44596    44592       -4     
- Misses      16577    16591      +14     
- Partials     3300     3303       +3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions github-actions Bot added size/S Small PR: 100-299 lines changed and removed size/S Small PR: 100-299 lines changed labels Jun 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/S Small PR: 100-299 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

LocalStore.getFilePath lacks path traversal validation

1 participant