Skip to content

Commit f91c211

Browse files
committed
fix: bound push_files tree traversal to 64 components
Refs #2578
1 parent 965edf4 commit f91c211

2 files changed

Lines changed: 36 additions & 0 deletions

File tree

‎pkg/github/push_files_test.go‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,3 +231,36 @@ func Test_PushFiles_NewBranchPreservesModes(t *testing.T) {
231231
require.Equal(t, "100755", f.entries[0].GetMode())
232232
require.Equal(t, []string{"POST /git/refs", "POST /git/trees", "POST /git/commits", "PATCH /git/refs/heads/main"}, f.writes)
233233
}
234+
235+
func Test_PushFiles_TreeTraversalLimit(t *testing.T) {
236+
for _, explicit := range []bool{false, true} {
237+
for _, depth := range []int{64, 65} {
238+
name := strings.Repeat("dir/", depth-1) + "run"
239+
t.Run(name+map[bool]string{false: " omitted", true: " explicit"}[explicit], func(t *testing.T) {
240+
f := pushFilesFixture{trees: map[string]*github.Tree{}, missingBranch: true}
241+
sha := "root"
242+
for i := 1; i < depth; i++ {
243+
next := sha + "-child"
244+
f.trees[sha] = &github.Tree{Entries: []*github.TreeEntry{{Path: new("dir"), Mode: new("040000"), Type: new("tree"), SHA: new(next)}}}
245+
sha = next
246+
}
247+
f.trees[sha] = &github.Tree{Entries: []*github.TreeEntry{pushFilesTreeEntry("run", "100755", "blob")}}
248+
file := map[string]any{"path": name, "content": "x"}
249+
if explicit {
250+
file["mode"] = "100755"
251+
}
252+
result := f.run(t, []any{file})
253+
if depth > 64 {
254+
require.True(t, result.IsError)
255+
require.Contains(t, result.Content[0].(*mcp.TextContent).Text, "exceeds Git tree traversal limit")
256+
require.Empty(t, f.treeReads)
257+
require.Empty(t, f.writes)
258+
} else {
259+
require.False(t, result.IsError)
260+
require.Len(t, f.treeReads, depth)
261+
require.Equal(t, "100755", f.entries[0].GetMode())
262+
}
263+
})
264+
}
265+
}
266+
}

‎pkg/github/repositories_helper.go‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ func resolvePushFilesModes(ctx context.Context, client *github.Client, owner, re
8181
for _, entry := range entries {
8282
treeSHA := baseTree
8383
parts := strings.Split(entry.GetPath(), "/")
84+
if len(parts) > 64 {
85+
return fmt.Errorf("path %q exceeds Git tree traversal limit", entry.GetPath())
86+
}
8487
mode := "100644"
8588
for i, part := range parts {
8689
treeEntries, cached := trees[treeSHA]

0 commit comments

Comments
 (0)