From 58f5fa653676fefc9b497e2cc18af976f749273c Mon Sep 17 00:00:00 2001
From: KN4CK3R <admin@oldschoolhack.me>
Date: Fri, 17 Nov 2023 12:17:33 +0100
Subject: [PATCH] Fix Matrix and MSTeams nil dereference (#28089)

Fixes #28088
Fixes #28094

Added missing tests.

---------

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
---
 services/webhook/dingtalk_test.go  | 15 +++++++++++++++
 services/webhook/discord_test.go   | 18 ++++++++++++++++++
 services/webhook/feishu_test.go    | 12 ++++++++++++
 services/webhook/general_test.go   | 30 ++++++++++++++++++++++++++++++
 services/webhook/matrix.go         |  6 +++---
 services/webhook/matrix_test.go    | 13 +++++++++++++
 services/webhook/msteams.go        |  7 ++++---
 services/webhook/msteams_test.go   | 27 +++++++++++++++++++++++++++
 services/webhook/packagist_test.go |  9 +++++++++
 services/webhook/slack_test.go     | 12 ++++++++++++
 services/webhook/telegram_test.go  | 12 ++++++++++++
 11 files changed, 155 insertions(+), 6 deletions(-)

diff --git a/services/webhook/dingtalk_test.go b/services/webhook/dingtalk_test.go
index 7289c751f3..a03fa46f14 100644
--- a/services/webhook/dingtalk_test.go
+++ b/services/webhook/dingtalk_test.go
@@ -188,6 +188,21 @@ func TestDingTalkPayload(t *testing.T) {
 		assert.Equal(t, "http://localhost:3000/test/repo", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL))
 	})
 
+	t.Run("Package", func(t *testing.T) {
+		p := packageTestPayload()
+
+		d := new(DingtalkPayload)
+		pl, err := d.Package(p)
+		require.NoError(t, err)
+		require.NotNil(t, pl)
+		require.IsType(t, &DingtalkPayload{}, pl)
+
+		assert.Equal(t, "Package created: GiteaContainer:latest by user1", pl.(*DingtalkPayload).ActionCard.Text)
+		assert.Equal(t, "Package created: GiteaContainer:latest by user1", pl.(*DingtalkPayload).ActionCard.Title)
+		assert.Equal(t, "view package", pl.(*DingtalkPayload).ActionCard.SingleTitle)
+		assert.Equal(t, "http://localhost:3000/user1/-/packages/container/GiteaContainer/latest", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL))
+	})
+
 	t.Run("Wiki", func(t *testing.T) {
 		p := wikiTestPayload()
 
diff --git a/services/webhook/discord_test.go b/services/webhook/discord_test.go
index 6a276e9e87..b567cbc395 100644
--- a/services/webhook/discord_test.go
+++ b/services/webhook/discord_test.go
@@ -211,6 +211,24 @@ func TestDiscordPayload(t *testing.T) {
 		assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL)
 	})
 
+	t.Run("Package", func(t *testing.T) {
+		p := packageTestPayload()
+
+		d := new(DiscordPayload)
+		pl, err := d.Package(p)
+		require.NoError(t, err)
+		require.NotNil(t, pl)
+		require.IsType(t, &DiscordPayload{}, pl)
+
+		assert.Len(t, pl.(*DiscordPayload).Embeds, 1)
+		assert.Equal(t, "Package created: GiteaContainer:latest", pl.(*DiscordPayload).Embeds[0].Title)
+		assert.Empty(t, pl.(*DiscordPayload).Embeds[0].Description)
+		assert.Equal(t, "http://localhost:3000/user1/-/packages/container/GiteaContainer/latest", pl.(*DiscordPayload).Embeds[0].URL)
+		assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name)
+		assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL)
+		assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL)
+	})
+
 	t.Run("Wiki", func(t *testing.T) {
 		p := wikiTestPayload()
 
diff --git a/services/webhook/feishu_test.go b/services/webhook/feishu_test.go
index a3182e82b0..98bc50dede 100644
--- a/services/webhook/feishu_test.go
+++ b/services/webhook/feishu_test.go
@@ -144,6 +144,18 @@ func TestFeishuPayload(t *testing.T) {
 		assert.Equal(t, "[test/repo] Repository created", pl.(*FeishuPayload).Content.Text)
 	})
 
+	t.Run("Package", func(t *testing.T) {
+		p := packageTestPayload()
+
+		d := new(FeishuPayload)
+		pl, err := d.Package(p)
+		require.NoError(t, err)
+		require.NotNil(t, pl)
+		require.IsType(t, &FeishuPayload{}, pl)
+
+		assert.Equal(t, "Package created: GiteaContainer:latest by user1", pl.(*FeishuPayload).Content.Text)
+	})
+
 	t.Run("Wiki", func(t *testing.T) {
 		p := wikiTestPayload()
 
diff --git a/services/webhook/general_test.go b/services/webhook/general_test.go
index a9a8c6b521..41bac3fd04 100644
--- a/services/webhook/general_test.go
+++ b/services/webhook/general_test.go
@@ -303,6 +303,36 @@ func repositoryTestPayload() *api.RepositoryPayload {
 	}
 }
 
+func packageTestPayload() *api.PackagePayload {
+	return &api.PackagePayload{
+		Action: api.HookPackageCreated,
+		Sender: &api.User{
+			UserName:  "user1",
+			AvatarURL: "http://localhost:3000/user1/avatar",
+		},
+		Repository: nil,
+		Organization: &api.User{
+			UserName:  "org1",
+			AvatarURL: "http://localhost:3000/org1/avatar",
+		},
+		Package: &api.Package{
+			Owner: &api.User{
+				UserName:  "user1",
+				AvatarURL: "http://localhost:3000/user1/avatar",
+			},
+			Repository: nil,
+			Creator: &api.User{
+				UserName:  "user1",
+				AvatarURL: "http://localhost:3000/user1/avatar",
+			},
+			Type:    "container",
+			Name:    "GiteaContainer",
+			Version: "latest",
+			HTMLURL: "http://localhost:3000/user1/-/packages/container/GiteaContainer/latest",
+		},
+	}
+}
+
 func TestGetIssuesPayloadInfo(t *testing.T) {
 	p := issueTestPayload()
 
diff --git a/services/webhook/matrix.go b/services/webhook/matrix.go
index ab7e6b72c2..602d16ef39 100644
--- a/services/webhook/matrix.go
+++ b/services/webhook/matrix.go
@@ -212,14 +212,14 @@ func (m *MatrixPayload) Repository(p *api.RepositoryPayload) (api.Payloader, err
 
 func (m *MatrixPayload) Package(p *api.PackagePayload) (api.Payloader, error) {
 	senderLink := MatrixLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
-	repoLink := MatrixLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
+	packageLink := MatrixLinkFormatter(p.Package.HTMLURL, p.Package.Name)
 	var text string
 
 	switch p.Action {
 	case api.HookPackageCreated:
-		text = fmt.Sprintf("[%s] Package published by %s", repoLink, senderLink)
+		text = fmt.Sprintf("[%s] Package published by %s", packageLink, senderLink)
 	case api.HookPackageDeleted:
-		text = fmt.Sprintf("[%s] Package deleted by %s", repoLink, senderLink)
+		text = fmt.Sprintf("[%s] Package deleted by %s", packageLink, senderLink)
 	}
 
 	return getMatrixPayload(text, nil, m.MsgType), nil
diff --git a/services/webhook/matrix_test.go b/services/webhook/matrix_test.go
index 8c71094228..99a22fbd7e 100644
--- a/services/webhook/matrix_test.go
+++ b/services/webhook/matrix_test.go
@@ -155,6 +155,19 @@ func TestMatrixPayload(t *testing.T) {
 		assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>] Repository created by <a href="https://try.gitea.io/user1">user1</a>`, pl.(*MatrixPayload).FormattedBody)
 	})
 
+	t.Run("Package", func(t *testing.T) {
+		p := packageTestPayload()
+
+		d := new(MatrixPayload)
+		pl, err := d.Package(p)
+		require.NoError(t, err)
+		require.NotNil(t, pl)
+		require.IsType(t, &MatrixPayload{}, pl)
+
+		assert.Equal(t, `[[GiteaContainer](http://localhost:3000/user1/-/packages/container/GiteaContainer/latest)] Package published by [user1](https://try.gitea.io/user1)`, pl.(*MatrixPayload).Body)
+		assert.Equal(t, `[<a href="http://localhost:3000/user1/-/packages/container/GiteaContainer/latest">GiteaContainer</a>] Package published by <a href="https://try.gitea.io/user1">user1</a>`, pl.(*MatrixPayload).FormattedBody)
+	})
+
 	t.Run("Wiki", func(t *testing.T) {
 		p := wikiTestPayload()
 
diff --git a/services/webhook/msteams.go b/services/webhook/msteams.go
index f58da3fe1c..37810b4cd3 100644
--- a/services/webhook/msteams.go
+++ b/services/webhook/msteams.go
@@ -316,11 +316,12 @@ func GetMSTeamsPayload(p api.Payloader, event webhook_module.HookEventType, _ st
 }
 
 func createMSTeamsPayload(r *api.Repository, s *api.User, title, text, actionTarget string, color int, fact *MSTeamsFact) *MSTeamsPayload {
-	facts := []MSTeamsFact{
-		{
+	facts := make([]MSTeamsFact, 0, 2)
+	if r != nil {
+		facts = append(facts, MSTeamsFact{
 			Name:  "Repository:",
 			Value: r.FullName,
-		},
+		})
 	}
 	if fact != nil {
 		facts = append(facts, *fact)
diff --git a/services/webhook/msteams_test.go b/services/webhook/msteams_test.go
index 990a535df5..8d1aed6040 100644
--- a/services/webhook/msteams_test.go
+++ b/services/webhook/msteams_test.go
@@ -329,6 +329,33 @@ func TestMSTeamsPayload(t *testing.T) {
 		assert.Equal(t, "http://localhost:3000/test/repo", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI)
 	})
 
+	t.Run("Package", func(t *testing.T) {
+		p := packageTestPayload()
+
+		d := new(MSTeamsPayload)
+		pl, err := d.Package(p)
+		require.NoError(t, err)
+		require.NotNil(t, pl)
+		require.IsType(t, &MSTeamsPayload{}, pl)
+
+		assert.Equal(t, "Package created: GiteaContainer:latest", pl.(*MSTeamsPayload).Title)
+		assert.Equal(t, "Package created: GiteaContainer:latest", pl.(*MSTeamsPayload).Summary)
+		assert.Len(t, pl.(*MSTeamsPayload).Sections, 1)
+		assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle)
+		assert.Empty(t, pl.(*MSTeamsPayload).Sections[0].Text)
+		assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 1)
+		for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts {
+			if fact.Name == "Package:" {
+				assert.Equal(t, p.Package.Name, fact.Value)
+			} else {
+				t.Fail()
+			}
+		}
+		assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1)
+		assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1)
+		assert.Equal(t, "http://localhost:3000/user1/-/packages/container/GiteaContainer/latest", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI)
+	})
+
 	t.Run("Wiki", func(t *testing.T) {
 		p := wikiTestPayload()
 
diff --git a/services/webhook/packagist_test.go b/services/webhook/packagist_test.go
index 932b56fe9b..26d01b0555 100644
--- a/services/webhook/packagist_test.go
+++ b/services/webhook/packagist_test.go
@@ -115,6 +115,15 @@ func TestPackagistPayload(t *testing.T) {
 		require.Nil(t, pl)
 	})
 
+	t.Run("Package", func(t *testing.T) {
+		p := packageTestPayload()
+
+		d := new(PackagistPayload)
+		pl, err := d.Package(p)
+		require.NoError(t, err)
+		require.Nil(t, pl)
+	})
+
 	t.Run("Wiki", func(t *testing.T) {
 		p := wikiTestPayload()
 
diff --git a/services/webhook/slack_test.go b/services/webhook/slack_test.go
index d9828f374f..b1340963e2 100644
--- a/services/webhook/slack_test.go
+++ b/services/webhook/slack_test.go
@@ -144,6 +144,18 @@ func TestSlackPayload(t *testing.T) {
 		assert.Equal(t, "[<http://localhost:3000/test/repo|test/repo>] Repository created by <https://try.gitea.io/user1|user1>", pl.(*SlackPayload).Text)
 	})
 
+	t.Run("Package", func(t *testing.T) {
+		p := packageTestPayload()
+
+		d := new(SlackPayload)
+		pl, err := d.Package(p)
+		require.NoError(t, err)
+		require.NotNil(t, pl)
+		require.IsType(t, &SlackPayload{}, pl)
+
+		assert.Equal(t, "Package created: <http://localhost:3000/user1/-/packages/container/GiteaContainer/latest|GiteaContainer:latest> by <https://try.gitea.io/user1|user1>", pl.(*SlackPayload).Text)
+	})
+
 	t.Run("Wiki", func(t *testing.T) {
 		p := wikiTestPayload()
 
diff --git a/services/webhook/telegram_test.go b/services/webhook/telegram_test.go
index b42b0ccda8..5b9927d057 100644
--- a/services/webhook/telegram_test.go
+++ b/services/webhook/telegram_test.go
@@ -144,6 +144,18 @@ func TestTelegramPayload(t *testing.T) {
 		assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>] Repository created`, pl.(*TelegramPayload).Message)
 	})
 
+	t.Run("Package", func(t *testing.T) {
+		p := packageTestPayload()
+
+		d := new(TelegramPayload)
+		pl, err := d.Package(p)
+		require.NoError(t, err)
+		require.NotNil(t, pl)
+		require.IsType(t, &TelegramPayload{}, pl)
+
+		assert.Equal(t, `Package created: <a href="http://localhost:3000/user1/-/packages/container/GiteaContainer/latest">GiteaContainer:latest</a> by <a href="https://try.gitea.io/user1">user1</a>`, pl.(*TelegramPayload).Message)
+	})
+
 	t.Run("Wiki", func(t *testing.T) {
 		p := wikiTestPayload()