Mask the http target's destination URL in the UI (closes #115)
All checks were successful
check / check (push) Successful in 4m17s

An http target's destination is frequently a Slack, Discord or
Teams incoming-webhook endpoint whose path segments are the
credential — the same property that made the Slack target's
webhook URL a bearer token. The source detail page rendered it
in full, so the leak closed for slack targets stayed reachable
through a different target type.

Render it through the existing MaskURL, which reduces a URL to
scheme and host. The field accepts an arbitrary URL, so no path
segment can be assumed non-secret and none is shown.
This commit is contained in:
2026-08-12 09:40:41 +00:00
parent d19e33671c
commit 5c0ea2b44f
3 changed files with 80 additions and 4 deletions

View File

@@ -19,6 +19,7 @@ const (
viewExampleOrigin = "https://example.com"
viewExampleHook = viewExampleOrigin + "/hook"
viewMaskedOrigin = viewExampleOrigin + "/..."
viewUnavailable = "(unavailable)"
viewExpiryNever = "never"
)
@@ -162,7 +163,7 @@ func TestNewTargetViews_HTTP(t *testing.T) {
assert.Equal(
t,
map[string]string{
"Destination URL": viewExampleHook,
"Destination URL": viewMaskedOrigin,
"Timeout": "30s",
"Headers": "1 configured",
"Max Retries": "5",
@@ -188,13 +189,41 @@ func TestNewTargetViews_HTTPFireAndForget(t *testing.T) {
assert.Equal(
t,
map[string]string{
"Destination URL": viewExampleHook,
"Destination URL": viewMaskedOrigin,
"Max Retries": "0 (fire-and-forget)",
},
fieldMap(view.Config),
)
}
// TestNewTargetViews_HTTPMasksDestinationURL proves the rule
// holds for the http target too: an http destination is
// routinely an incoming-webhook endpoint whose path segments
// are the credential, so none of them is shown.
func TestNewTargetViews_HTTPMasksDestinationURL(t *testing.T) {
t.Parallel()
view := viewFor(t, database.Target{
Type: database.TargetTypeHTTP,
Config: `{"url":"` + slackWebhookURL + `"}`,
})
fields := fieldMap(view.Config)
assert.Equal(
t,
"https://hooks.slack.com/...",
fields["Destination URL"],
)
for _, v := range fields {
assert.NotContains(t, v, slackSecretPath)
assert.NotContains(t, v, "T00000000")
assert.NotContains(t, v, "B00000000")
assert.NotContains(t, v, "XXXXXXXXXXXXXXXXXXXXXXXX")
}
}
func TestNewTargetViews_Database(t *testing.T) {
t.Parallel()