Skip to content

Commit

Permalink
add escaping of comment text in webhook default JSON template
Browse files Browse the repository at this point in the history
  • Loading branch information
paskal authored and umputun committed Jul 2, 2024
1 parent 8df986e commit 95966f6
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 134 deletions.
14 changes: 12 additions & 2 deletions backend/app/notify/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package notify
import (
"bytes"
"context"
"encoding/json"
"fmt"
"text/template"
"time"
Expand All @@ -12,7 +13,7 @@ import (
)

const (
webhookDefaultTemplate = `{"text": "{{.Text}}"}`
webhookDefaultTemplate = `{"text": {{.Text | escapeJSONString}}}`
)

// WebhookParams contain settings for webhook notifications
Expand Down Expand Up @@ -49,7 +50,7 @@ func NewWebhook(params WebhookParams) (*Webhook, error) {
params.Template = webhookDefaultTemplate
}

payloadTmpl, err := template.New("webhook").Parse(params.Template)
payloadTmpl, err := template.New("webhook").Funcs(template.FuncMap{"escapeJSONString": escapeJSONString}).Parse(params.Template)
if err != nil {
return nil, fmt.Errorf("unable to parse webhook template: %w", err)
}
Expand Down Expand Up @@ -82,3 +83,12 @@ func (w *Webhook) SendVerification(_ context.Context, _ VerificationRequest) err
func (w *Webhook) String() string {
return fmt.Sprintf("%s to %s", w.Webhook.String(), w.url)
}

// escapeJSONString escapes string for JSON
func escapeJSONString(s string) (string, error) {
b, err := json.Marshal(s)
if err != nil {
return "", err
}
return string(b), nil
}
29 changes: 29 additions & 0 deletions backend/app/notify/webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package notify

import (
"context"
"io"
"net/http"
"net/http/httptest"
"testing"
"time"

Expand Down Expand Up @@ -34,6 +37,32 @@ func TestWebhook_NewWebhook(t *testing.T) {
assert.Contains(t, err.Error(), "unable to parse webhook template")
}

// https://github.com/umputun/remark42/issues/1791
func TestWebhook_ReceiveValidJSON(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
assert.Equal(t, r.URL.Path, "/webhook-notify")
assert.Equal(t, "POST", r.Method)
body, err := io.ReadAll(r.Body)
assert.NoError(t, err)
t.Log("received body", string(body))
assert.JSONEq(t, `{"text": "<p>testme</p>\n"}`, string(body))
}))
defer ts.Close()

wh, err := NewWebhook(WebhookParams{
URL: ts.URL + "/webhook-notify",
Headers: []string{"Content-Type:application/json,text/plain"},
})
assert.NoError(t, err)
assert.NotNil(t, wh)

f := store.NewCommentFormatter()
c := store.Comment{Text: f.FormatText("testme", false), ParentID: "1", ID: "999"}

err = wh.Send(context.Background(), Request{Comment: c})
assert.NoError(t, err)
}

func TestWebhook_Send(t *testing.T) {
wh, err := NewWebhook(WebhookParams{
URL: "bad-url",
Expand Down
2 changes: 1 addition & 1 deletion site/src/docs/configuration/notifications/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,4 @@ If all goes fine, you should be able to see the following message on your Slack

You need to set `NOTIFY_ADMINS=webhook` to enable WebHook notifications on all new comments and set at least `NOTIFY_WEBHOOK_URL` for them to start working.

Additionally, you might want to set `NOTIFY_WEBHOOK_TEMPLATE` (which is Go Template, `{"text": "{{.Text}}"}` by default) and `NOTIFY_WEBHOOK_HEADERS`, which is HTTP header(s) in format `Header1:Value1,Header2:Value2,...`.
Additionally, you might want to set `NOTIFY_WEBHOOK_TEMPLATE` (which is Go Template, `{"text": {{.Text | escapeJSONString}}}` by default) and `NOTIFY_WEBHOOK_HEADERS`, which is HTTP header(s) in format `Header1:Value1,Header2:Value2,...`.
Loading

0 comments on commit 95966f6

Please sign in to comment.