diff --git a/internal/handlers/source_detail_test.go b/internal/handlers/source_detail_test.go
index 007f464..2d58837 100644
--- a/internal/handlers/source_detail_test.go
+++ b/internal/handlers/source_detail_test.go
@@ -275,3 +275,99 @@ func TestHandleSourceDetail_FitsWideAndNarrowWindows(t *testing.T) {
`
`,
)
}
+
+// TestHandleSourceDetail_DeletePromptsNameWhatIsLost checks that each
+// delete prompt on the webhook page names the webhook, entrypoint or
+// target and says what deleting it loses, that the webhook's gives its
+// number of stored events (5 received, 2 removed by retention, so 3,
+// the statistics pane's "Within retention" figure), and that an
+// entrypoint with no description is named by its URL. The template
+// writes the slashes after http: as \/, which the browser reads as /.
+func TestHandleSourceDetail_DeletePromptsNameWhatIsLost(t *testing.T) {
+ t.Parallel()
+
+ var (
+ h *handlers.Handlers
+ sess *session.Session
+ db *database.Database
+ dbMgr *database.WebhookDBManager
+ )
+
+ app := newTestApp(t, &h, &sess, &db, &dbMgr)
+ app.RequireStart()
+
+ t.Cleanup(app.RequireStop)
+
+ wh := seedWebhook(t, db)
+
+ webhookDB, err := dbMgr.GetDB(wh.ID)
+ require.NoError(t, err)
+ require.NoError(t, database.AddEventTotals(
+ webhookDB, database.EventTotals{Events: 5, EventsRemoved: 2},
+ ))
+
+ unnamed := seedEntrypoint(t, db, wh.ID)
+ require.NoError(t, db.DB().Omit(clause.Associations).Create(
+ &database.Entrypoint{
+ WebhookID: wh.ID,
+ Path: "described-" + wh.ID,
+ Description: "Stripe",
+ Active: true,
+ },
+ ).Error)
+ seedTarget(t, db, wh.ID, database.TargetTypeLog)
+
+ body := renderSourceDetailPage(t, h, sess, wh.ID)
+
+ assert.Contains(t, body,
+ `Delete webhook "delete-me"?\n\n`+
+ `This deletes its stored events (3) and their deliveries. `+
+ `Any archive files it wrote are kept.`)
+ assert.Contains(t, body,
+ `Delete entrypoint "Stripe"?\n\n`+
+ `Senders using its URL get an error from now on, `+
+ `and the URL cannot be restored.`)
+ assert.Contains(t, body,
+ `Delete entrypoint "http:\/\/example.com/h/`+
+ unnamed.Path+`"?`)
+ assert.Contains(t, body,
+ `Delete target "t-log"?\n\n`+
+ `Nothing more is delivered to it. `+
+ `Its past deliveries stay in the event log.`)
+}
+
+// TestHandleSourceDetail_DeletePromptKeepsQuotesInName checks that a
+// webhook name with quotes, a backslash, a closing script tag and a
+// newline reaches its delete prompt escaped for the script, which the
+// browser reads back as the name typed: each quote and angle bracket
+// as a \u escape, the slash as \/, the newline as \n and the backslash
+// doubled. An unescaped newline would break the prompt's script, and
+// the form would then submit without asking.
+func TestHandleSourceDetail_DeletePromptKeepsQuotesInName(t *testing.T) {
+ t.Parallel()
+
+ var (
+ h *handlers.Handlers
+ sess *session.Session
+ db *database.Database
+ )
+
+ app := newTestApp(t, &h, &sess, &db)
+ app.RequireStart()
+
+ t.Cleanup(app.RequireStop)
+
+ wh := &database.Webhook{
+ UserID: deleteTestUserID,
+ Name: "Bob's \"best\" \\ hook\nline two",
+ }
+ require.NoError(
+ t, db.DB().Omit(clause.Associations).Create(wh).Error,
+ )
+
+ body := renderSourceDetailPage(t, h, sess, wh.ID)
+
+ assert.Contains(t, body,
+ "Delete webhook "Bob\\u0027s \\u0022best\\u0022 \\\\ hook"+
+ "\\u003c\\/script\\u003e\\nline two"?")
+}
diff --git a/internal/handlers/source_management.go b/internal/handlers/source_management.go
index 01dcb50..5b6049c 100644
--- a/internal/handlers/source_management.go
+++ b/internal/handlers/source_management.go
@@ -612,8 +612,9 @@ func (h *Handlers) renderSourceDetail(
// The host is the client's Host header, unvalidated. It is
// inert only because source_detail.html renders BaseURL as
- // text inside a
element; putting it in an href or any
- // other URL context needs it constrained first.
+ // text, inside a element and in an entrypoint's delete
+ // prompt; putting it in an href or any other URL context
+ // needs it constrained first.
baseURL := scheme + "://" + r.Host
// The template calls Webhook methods, which take pointer
diff --git a/templates/source_detail.html b/templates/source_detail.html
index a27c382..eeea81a 100644
--- a/templates/source_detail.html
+++ b/templates/source_detail.html
@@ -20,7 +20,11 @@
Full Event Log
Edit
-
@@ -83,7 +87,7 @@
{{if .Active}}Deactivate{{else}}Activate{{end}}
-
@@ -249,7 +253,7 @@
{{if .Active}}Deactivate{{else}}Activate{{end}}
-