Skip to content

Commit 7172f3e

Browse files
harcheclaude
andcommitted
Fix path traversal vulnerabilities in update-readme tool
Add input validation to prevent path traversal attacks in the update-readme internal tool: - Clean file path using filepath.Clean to remove path traversal sequences - Validate that only README.md files can be updated - Add argument count validation This fixes Snyk code scan findings: - MEDIUM severity path traversal in os.ReadFile (line 28) - MEDIUM severity path traversal in os.WriteFile (line 84) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 7107a24 commit 7172f3e

File tree

1 file changed

+58
-2
lines changed
  • internal/tools/update-readme

1 file changed

+58
-2
lines changed

internal/tools/update-readme/main.go

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"maps"
77
"os"
8+
"path/filepath"
89
"slices"
910
"strings"
1011

@@ -25,7 +26,16 @@ func (o *OpenShift) IsOpenShift(ctx context.Context) bool {
2526
var _ internalk8s.Openshift = (*OpenShift)(nil)
2627

2728
func main() {
28-
readme, err := os.ReadFile(os.Args[1])
29+
if len(os.Args) < 2 {
30+
panic("Usage: update-readme <path-to-readme>")
31+
}
32+
33+
readmePath, err := resolveReadmePath(os.Args[1])
34+
if err != nil {
35+
panic(err)
36+
}
37+
38+
readme, err := os.ReadFile(readmePath)
2939
if err != nil {
3040
panic(err)
3141
}
@@ -81,11 +91,57 @@ func main() {
8191
toolsetTools.String(),
8292
)
8393

84-
if err := os.WriteFile(os.Args[1], []byte(updated), 0o644); err != nil {
94+
if err := os.WriteFile(readmePath, []byte(updated), 0o644); err != nil {
8595
panic(err)
8696
}
8797
}
8898

99+
func resolveReadmePath(arg string) (string, error) {
100+
// Sanitize the file path to prevent path traversal
101+
cleanPath := filepath.Clean(arg)
102+
103+
// Validate that the file path is README.md to prevent arbitrary file access
104+
if filepath.Base(cleanPath) != "README.md" {
105+
return "", fmt.Errorf("Error: This tool can only update README.md files")
106+
}
107+
108+
repoRoot, err := os.Getwd()
109+
if err != nil {
110+
return "", fmt.Errorf("determine working directory: %w", err)
111+
}
112+
113+
absoluteRepoRoot, err := filepath.Abs(repoRoot)
114+
if err != nil {
115+
return "", fmt.Errorf("resolve working directory: %w", err)
116+
}
117+
118+
resolvedRepoRoot, err := filepath.EvalSymlinks(absoluteRepoRoot)
119+
if err != nil {
120+
resolvedRepoRoot = absoluteRepoRoot
121+
}
122+
123+
absoluteReadmePath, err := filepath.Abs(cleanPath)
124+
if err != nil {
125+
return "", fmt.Errorf("resolve README path: %w", err)
126+
}
127+
128+
resolvedReadmePath, err := filepath.EvalSymlinks(absoluteReadmePath)
129+
if err != nil {
130+
return "", fmt.Errorf("resolve README path: %w", err)
131+
}
132+
133+
relativePath, err := filepath.Rel(resolvedRepoRoot, resolvedReadmePath)
134+
if err != nil {
135+
return "", fmt.Errorf("determine README location: %w", err)
136+
}
137+
138+
if relativePath == ".." || strings.HasPrefix(relativePath, ".."+string(os.PathSeparator)) {
139+
return "", fmt.Errorf("Error: README path must stay within repository root")
140+
}
141+
142+
return resolvedReadmePath, nil
143+
}
144+
89145
func replaceBetweenMarkers(content, startMarker, endMarker, replacement string) string {
90146
startIdx := strings.Index(content, startMarker)
91147
if startIdx == -1 {

0 commit comments

Comments
 (0)