Skip to content

Commit e4857b0

Browse files
authored
Fix GitRepo URL matching in webhook server (#4777)
The webhook server would previously erroneously match an incoming request's repository URL with a GitRepo whose spec repo amounted to that URL with a suffix. The regular expression for repository URL matching no longer tolerates suffixes.
1 parent 6d9e259 commit e4857b0

File tree

2 files changed

+102
-1
lines changed

2 files changed

+102
-1
lines changed

pkg/webhook/webhook.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ func (w *Webhook) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
109109
path := strings.Replace(u.EscapedPath()[1:], "/_git/", "(/_git)?/", 1)
110110

111111
regexpStr := `(?i)(http://|https://|\w+@|ssh://(\w+@)?|git@(ssh\.)?)` + u.Hostname() +
112-
"(:[0-9]+|)[:/](v\\d/)?" + path + "(\\.git)?"
112+
"(:[0-9]+|)[:/](v\\d/)?" + path + "(\\.git)?$"
113113
repoRegexp, err := regexp.Compile(regexpStr)
114114
if err != nil {
115115
w.logAndReturn(rw, err)

pkg/webhook/webhook_test.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,107 @@ func TestGitHubSecretAndCommitUpdated(t *testing.T) {
766766
}
767767
}
768768

769+
func TestGitRepoURLMatch(t *testing.T) {
770+
ctlr := gomock.NewController(t)
771+
mockClient := mocks.NewMockK8sClient(ctlr)
772+
773+
expectedCommit := "af69d162de5a276abc86e0686b2b44033cd3f442"
774+
775+
gitRepos := []v1alpha1.GitRepo{
776+
{
777+
ObjectMeta: metav1.ObjectMeta{
778+
Name: "intended-gitrepo",
779+
Namespace: "my-namespace",
780+
},
781+
Spec: v1alpha1.GitRepoSpec{
782+
Repo: "https://github.com/example/repo",
783+
},
784+
Status: v1alpha1.GitRepoStatus{
785+
WebhookCommit: "12345abcdef", // different from expectedCommit
786+
},
787+
},
788+
{
789+
ObjectMeta: metav1.ObjectMeta{
790+
Name: "gitrepo-which-should-be-ignored",
791+
Namespace: "my-namespace",
792+
},
793+
Spec: v1alpha1.GitRepoSpec{
794+
Repo: "https://github.com/example/repo-with-suffix",
795+
},
796+
Status: v1alpha1.GitRepoStatus{
797+
WebhookCommit: "12345abcdef", // different from expectedCommit
798+
},
799+
},
800+
}
801+
802+
// List GitRepos mock call
803+
mockClient.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().DoAndReturn(
804+
func(ctx context.Context, list *v1alpha1.GitRepoList, opts ...client.ListOption) error {
805+
list.Items = append(list.Items, gitRepos...)
806+
807+
return nil
808+
},
809+
)
810+
811+
nn := types.NamespacedName{Name: webhookSecretName, Namespace: "my-namespace"}
812+
// The following calls should happen only _once_, for the GitRepo with the exact URL match, hence the explicit
813+
// `.Times(1)` calls.
814+
mockClient.EXPECT().Get(gomock.Any(), nn, gomock.Any()).Return(errors.NewNotFound(schema.GroupResource{}, "")).Times(1)
815+
816+
mockClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(
817+
func(ctx context.Context, name types.NamespacedName, gitrepo *v1alpha1.GitRepo, _ ...interface{}) error {
818+
// check that the GitRepo is the expected one
819+
if name.Name != "intended-gitrepo" {
820+
t.Errorf("wrong gitrepo matched: expected 'intended-gitrepo', got %s", name.Name)
821+
}
822+
823+
return nil
824+
},
825+
).Times(1)
826+
statusClient := mocks.NewMockStatusWriter(ctlr)
827+
mockClient.EXPECT().Status().Return(statusClient).Times(1)
828+
statusClient.EXPECT().Patch(gomock.Any(), gomock.Any(), gomock.Any()).Do(
829+
func(ctx context.Context, repo *v1alpha1.GitRepo, _ client.Patch, opts ...interface{}) {
830+
// check that the commit is the expected one
831+
if repo.Status.WebhookCommit != expectedCommit {
832+
t.Errorf("expecting gitrepo webhook commit %s, got %s", expectedCommit, repo.Status.WebhookCommit)
833+
}
834+
if repo.Spec.PollingInterval.Duration != time.Hour {
835+
t.Errorf("expecting gitrepo polling interval 1h, got %s", repo.Spec.PollingInterval.Duration)
836+
}
837+
},
838+
).Times(1)
839+
840+
// we set only the values that we're going to use in the push event to make things simple
841+
jsonBody := []byte(fmt.Sprintf(`
842+
{
843+
"ref":"refs/heads/main",
844+
"after":"%s",
845+
"repository":{
846+
"html_url":"https://github.com/example/repo"
847+
}
848+
}`, expectedCommit))
849+
850+
// Request creation
851+
req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, "/", bytes.NewReader(jsonBody))
852+
if err != nil {
853+
t.Fatalf("Failed to create HTTP request: %v", err)
854+
}
855+
req.Header.Set("X-Github-Event", "push")
856+
857+
rr := httptest.NewRecorder()
858+
w := &Webhook{
859+
client: mockClient,
860+
namespace: "my-namespace",
861+
}
862+
w.ServeHTTP(rr, req)
863+
864+
// Verify the response status code is correct
865+
if status := rr.Code; status != http.StatusOK {
866+
t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK)
867+
}
868+
}
869+
769870
func TestErrorReadingRequest(t *testing.T) {
770871
ctlr := gomock.NewController(t)
771872
mockClient := mocks.NewMockK8sClient(ctlr)

0 commit comments

Comments
 (0)