Skip to content

Commit 87ead3a

Browse files
fix(repos): escape the path in create_or_update_file
A path containing # or ? was sent unescaped, so the file was written to the part of the path before that character (docs/C#/intro.md -> docs/C).
1 parent 71ef826 commit 87ead3a

2 files changed

Lines changed: 35 additions & 1 deletion

File tree

‎pkg/github/repositories.go‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,8 @@ SHA MUST be provided for existing file updates.
605605
// If file not found, no previous SHA needed (new file creation)
606606
}
607607

608-
fileContent, resp, err := client.Repositories.CreateFile(ctx, owner, repo, path, opts)
608+
// CreateFile puts path into the URL as is, unlike GetContents.
609+
fileContent, resp, err := client.Repositories.CreateFile(ctx, owner, repo, escapeGitTreeish(path), opts)
609610
if err != nil {
610611
return ghErrors.NewGitHubAPIErrorResponse(ctx,
611612
"failed to create/update file",

‎pkg/github/repositories_test.go‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6823,3 +6823,36 @@ func Test_ListRepositoryCollaborators(t *testing.T) {
68236823
})
68246824
}
68256825
}
6826+
6827+
func Test_CreateOrUpdateFile_EscapesPath(t *testing.T) {
6828+
serverTool := CreateOrUpdateFile(translations.NullTranslationHelper)
6829+
6830+
var putPath string
6831+
mockedClient := MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
6832+
"": func(w http.ResponseWriter, r *http.Request) {
6833+
if r.Method == http.MethodPut {
6834+
putPath = r.URL.EscapedPath()
6835+
mockResponse(t, http.StatusCreated, &github.RepositoryContentResponse{
6836+
Content: &github.RepositoryContent{Path: new("docs/C#/intro?.md")},
6837+
})(w, r)
6838+
return
6839+
}
6840+
mockResponse(t, http.StatusNotFound, map[string]string{"message": "Not Found"})(w, r)
6841+
},
6842+
})
6843+
deps := BaseDeps{Client: mustNewGHClient(t, mockedClient)}
6844+
handler := serverTool.Handler(deps)
6845+
6846+
request := createMCPRequest(map[string]any{
6847+
"owner": "owner",
6848+
"repo": "repo",
6849+
"path": "docs/C#/intro?.md",
6850+
"content": "# Intro",
6851+
"message": "Add intro",
6852+
"branch": "main",
6853+
})
6854+
result, err := handler(ContextWithDeps(context.Background(), deps), &request)
6855+
require.NoError(t, err)
6856+
require.False(t, result.IsError, getTextResult(t, result).Text)
6857+
assert.Equal(t, "/repos/owner/repo/contents/docs/C%23/intro%3F.md", putPath)
6858+
}

0 commit comments

Comments
 (0)