diff --git a/router/v3/router.go b/router/v3/router.go index a1b80c85a..cdc16262f 100644 --- a/router/v3/router.go +++ b/router/v3/router.go @@ -292,10 +292,7 @@ func (h *Handlers) Setup(e *echo.Group) { apiWebhooksWID.GET("/icon", h.GetWebhookIcon, requires(permission.GetWebhook)) apiWebhooksWID.PUT("/icon", h.ChangeWebhookIcon, requires(permission.EditWebhook)) apiWebhooksWID.GET("/messages", h.GetWebhookMessages, requires(permission.GetWebhook)) - apiWebhooksWIDMessage := apiWebhooksWID.Group("/messages/:messageID", requires(permission.GetWebhook), retrieve.MessageID(), requiresMessageAccessPerm) - { - apiWebhooksWIDMessage.DELETE("", h.DeleteWebhookMessage, requires(permission.GetMessage)) - } + } } apiGroups := api.Group("/groups") @@ -421,7 +418,14 @@ func (h *Handlers) Setup(e *echo.Group) { } apiNoAuth.POST("/login", h.Login, noLogin) apiNoAuth.POST("/logout", h.Logout) - apiNoAuth.POST("/webhooks/:webhookID", h.PostWebhook, retrieve.WebhookID()) + apiWebhooks := apiNoAuth.Group("/webhooks/:webhookID", retrieve.WebhookID()) + { + apiWebhooks.POST("", h.PostWebhook) + apiWebhooksMessages := apiWebhooks.Group("/messages/:messageID", retrieve.MessageID()) + { + apiWebhooksMessages.DELETE("", h.DeleteWebhookMessage) + } + } apiNoAuth.POST("/qall/webhook", h.LiveKitWebhook) apiNoAuthPublic := apiNoAuth.Group("/public") { diff --git a/router/v3/webhooks.go b/router/v3/webhooks.go index 2ca09ddfb..42a55f292 100644 --- a/router/v3/webhooks.go +++ b/router/v3/webhooks.go @@ -255,6 +255,17 @@ func (h *Handlers) DeleteWebhookMessage(c echo.Context) error { botUserID := w.GetBotUserID() messageUserID := m.GetUserID() + // Webhookシークレット確認 + if len(w.GetSecret()) > 0 { + sig, _ := hex.DecodeString(c.Request().Header.Get(consts.HeaderSignature)) + if len(sig) == 0 { + return herror.BadRequest("missing X-TRAQ-Signature header") + } + if subtle.ConstantTimeCompare(hmac.SHA1([]byte{}, w.GetSecret()), sig) != 1 { + return herror.BadRequest("X-TRAQ-Signature is wrong") + } + } + if botUserID == messageUserID { if err := h.Repo.DeleteMessage(messageID); err != nil { return herror.InternalServerError(err) diff --git a/router/v3/webhooks_test.go b/router/v3/webhooks_test.go index d7f175101..51304985e 100644 --- a/router/v3/webhooks_test.go +++ b/router/v3/webhooks_test.go @@ -551,21 +551,36 @@ func TestHandlers_DeleteWebhookMessage(t *testing.T) { wh := env.CreateWebhook(t, rand, user.GetID(), ch.ID) wh2 := env.CreateWebhook(t, rand, user.GetID(), ch.ID) message := env.CreateMessage(t, wh.GetBotUserID(), ch.ID, "test") - s := env.S(t, user.GetID()) - t.Run("not logged in", func(t *testing.T) { + calcHMACSHA1 := func(t *testing.T, message, secret string) string { + t.Helper() + mac := hmac.New(sha1.New, []byte(secret)) + _, _ = mac.Write([]byte(message)) + return hex.EncodeToString(mac.Sum(nil)) + } + + t.Run("bad request (no signature)", func(t *testing.T) { t.Parallel() e := env.R(t) e.DELETE(path, wh.GetID(), message.GetID()). Expect(). - Status(http.StatusUnauthorized) + Status(http.StatusBadRequest) + }) + + t.Run("bad request (bad signature)", func(t *testing.T) { + t.Parallel() + e := env.R(t) + e.DELETE(path, wh.GetID(), message.GetID()). + WithHeader("X-TRAQ-Signature", calcHMACSHA1(t, "test", wh.GetSecret())). + Expect(). + Status(http.StatusBadRequest) }) t.Run("not found webhook", func(t *testing.T) { t.Parallel() e := env.R(t) e.DELETE(path, uuid.Must(uuid.NewV4()), message.GetID()). - WithCookie(session.CookieName, s). + WithHeader("X-TRAQ-Signature", calcHMACSHA1(t, "", wh.GetSecret())). Expect(). Status(http.StatusNotFound) }) @@ -574,7 +589,7 @@ func TestHandlers_DeleteWebhookMessage(t *testing.T) { t.Parallel() e := env.R(t) e.DELETE(path, wh.GetID(), uuid.Must(uuid.NewV4())). - WithCookie(session.CookieName, s). + WithHeader("X-TRAQ-Signature", calcHMACSHA1(t, "", wh.GetSecret())). Expect(). Status(http.StatusNotFound) }) @@ -583,7 +598,7 @@ func TestHandlers_DeleteWebhookMessage(t *testing.T) { t.Parallel() e := env.R(t) e.DELETE(path, wh2.GetID(), message.GetID()). - WithCookie(session.CookieName, s). + WithHeader("X-TRAQ-Signature", calcHMACSHA1(t, "", wh2.GetSecret())). Expect(). Status(http.StatusForbidden) }) @@ -591,12 +606,13 @@ func TestHandlers_DeleteWebhookMessage(t *testing.T) { t.Run("success", func(t *testing.T) { t.Parallel() e := env.R(t) - e.DELETE(path, wh.GetID(), message.GetID()). - WithCookie(session.CookieName, s). + message2 := env.CreateMessage(t, wh.GetBotUserID(), ch.ID, "test") + e.DELETE(path, wh.GetID(), message2.GetID()). + WithHeader("X-TRAQ-Signature", calcHMACSHA1(t, "", wh.GetSecret())). Expect(). Status(http.StatusNoContent) - _, err := env.Repository.GetMessageByID(message.GetID()) + _, err := env.Repository.GetMessageByID(message2.GetID()) assert.ErrorIs(t, err, repository.ErrNotFound) }) }