Skip to content

Commit d2b0141

Browse files
committed
webhook: use regexp.QuoteMeta for hostname and path
1 parent 85f1637 commit d2b0141

2 files changed

Lines changed: 82 additions & 4 deletions

File tree

pkg/webhook/webhook.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,8 @@ func (w *Webhook) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
105105
w.logAndReturn(rw, err)
106106
return
107107
}
108-
109-
path := strings.Replace(u.EscapedPath()[1:], "/_git/", "(/_git)?/", 1)
110-
111-
regexpStr := `(?i)(http://|https://|\w+@|ssh://(\w+@)?|git@(ssh\.)?)` + u.Hostname() +
108+
path := strings.Replace(regexp.QuoteMeta(u.EscapedPath()[1:]), `/_git/`, `(/_git)?/`, 1)
109+
regexpStr := `(?i)(http://|https://|\w+@|ssh://(\w+@)?|git@(ssh\.)?)` + regexp.QuoteMeta(u.Hostname()) +
112110
"(:[0-9]+|)[:/](v\\d/)?" + path + "(\\.git)?$"
113111
repoRegexp, err := regexp.Compile(regexpStr)
114112
if err != nil {

pkg/webhook/webhook_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -902,3 +902,83 @@ func TestErrorReadingRequest(t *testing.T) {
902902
t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusInternalServerError)
903903
}
904904
}
905+
906+
// TestGitHubWildcardURLDoesNotMatchUnintendedRepo verifies that regex metacharacters
907+
// in a webhook payload's repo URL (e.g. a hostname or path of ".*") are treated as
908+
// literals and do not accidentally match GitRepos whose URLs are unrelated to the
909+
// webhook source.
910+
func TestGitHubWildcardURLDoesNotMatchUnintendedRepo(t *testing.T) {
911+
tests := []struct {
912+
name string
913+
gitRepoURL string // the GitRepo's configured repo
914+
webhookRepoURL string // the repo URL embedded in the webhook payload
915+
}{
916+
{
917+
name: "wildcard hostname does not match unrelated gitrepo",
918+
gitRepoURL: "https://github.com/example/repo",
919+
webhookRepoURL: "https://.*/example/repo",
920+
},
921+
{
922+
name: "wildcard path does not match unrelated gitrepo",
923+
gitRepoURL: "https://github.com/example/repo",
924+
webhookRepoURL: "https://github.com/.*/.*",
925+
},
926+
{
927+
name: "wildcard hostname and path do not match unrelated gitrepo",
928+
gitRepoURL: "https://github.com/example/repo",
929+
webhookRepoURL: "https://.*/.*",
930+
},
931+
}
932+
933+
for _, tt := range tests {
934+
t.Run(tt.name, func(t *testing.T) {
935+
sch := runtime.NewScheme()
936+
if err := v1alpha1.AddToScheme(sch); err != nil {
937+
t.Fatalf("unable to add to scheme: %v", err)
938+
}
939+
utilruntime.Must(corev1.AddToScheme(sch))
940+
941+
gitRepo := &v1alpha1.GitRepo{
942+
ObjectMeta: metav1.ObjectMeta{
943+
Name: "test",
944+
Namespace: "default",
945+
},
946+
Spec: v1alpha1.GitRepoSpec{
947+
Repo: tt.gitRepoURL,
948+
Branch: "main",
949+
},
950+
}
951+
fakeClient := cfake.NewClientBuilder().
952+
WithScheme(sch).
953+
WithRuntimeObjects(gitRepo).
954+
WithStatusSubresource(gitRepo).
955+
Build()
956+
957+
w := &Webhook{client: fakeClient, namespace: "default"}
958+
959+
commit := "af69d162de5a276abc86e0686b2b44033cd3f442"
960+
jsonBody := fmt.Appendf(nil, `{
961+
"ref": "refs/heads/main",
962+
"after": "%s",
963+
"repository": {"html_url": "%s"}
964+
}`, commit, tt.webhookRepoURL)
965+
966+
req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, "/", bytes.NewReader(jsonBody))
967+
if err != nil {
968+
t.Fatalf("failed to create request: %v", err)
969+
}
970+
req.Header.Set("X-GitHub-Event", "push")
971+
972+
rr := httptest.NewRecorder()
973+
w.ServeHTTP(rr, req)
974+
975+
updated := &v1alpha1.GitRepo{}
976+
if err := fakeClient.Get(context.TODO(), types.NamespacedName{Name: gitRepo.Name, Namespace: gitRepo.Namespace}, updated); err != nil {
977+
t.Fatalf("failed to get gitrepo: %v", err)
978+
}
979+
if updated.Status.WebhookCommit != "" {
980+
t.Errorf("expected WebhookCommit to remain empty (no match), but got %q", updated.Status.WebhookCommit)
981+
}
982+
})
983+
}
984+
}

0 commit comments

Comments
 (0)