HTTP target destination URL is rendered in full and can itself be a bearer credential #115

Closed
opened 2026-08-11 14:28:51 +02:00 by clawbot · 1 comment
Collaborator

Surfaced while implementing #113, which masks the Slack target's webhook URL because it is a bearer credential.

The same property holds for http targets: a destination URL is frequently a Slack, Discord or Teams incoming-webhook endpoint, where the path segments are the secret. #113's done-criteria explicitly require showing the http destination URL as a labelled field, so it ships rendered in full — deliberate, and out of scope there, but it leaves the leak reachable via a different target type.

Needs your call, because masking unconditionally costs real usability: the destination is the one field an operator most needs to read back.

Options:

  1. Mask unconditionally, reusing maskURL from internal/delivery/target_config_view.go (scheme://host/...).
  2. Mask only credential-shaped URLs (known webhook hosts, or a path with high-entropy segments), showing the rest in full.
  3. Render in full and accept it, on the grounds that the operator configured it and the page is authenticated.

Recommendation: option 1. It is the same rule already applied to Slack, needs no heuristic that can be wrong in both directions, and the full value stays available in the edit form where it is actually needed.

Done-criteria once decided: a handler test asserting a secret-bearing http destination does not render its path segments on the source detail page.

Surfaced while implementing https://git.eeqj.de/sneak/webhooker/issues/113, which masks the Slack target's webhook URL because it is a bearer credential. The same property holds for `http` targets: a destination URL is frequently a Slack, Discord or Teams incoming-webhook endpoint, where the path segments *are* the secret. https://git.eeqj.de/sneak/webhooker/issues/113's done-criteria explicitly require showing the `http` destination URL as a labelled field, so it ships rendered in full — deliberate, and out of scope there, but it leaves the leak reachable via a different target type. Needs your call, because masking unconditionally costs real usability: the destination is the one field an operator most needs to read back. Options: 1. Mask unconditionally, reusing `maskURL` from `internal/delivery/target_config_view.go` (`scheme://host/...`). 2. Mask only credential-shaped URLs (known webhook hosts, or a path with high-entropy segments), showing the rest in full. 3. Render in full and accept it, on the grounds that the operator configured it and the page is authenticated. Recommendation: option 1. It is the same rule already applied to Slack, needs no heuristic that can be wrong in both directions, and the full value stays available in the edit form where it is actually needed. Done-criteria once decided: a handler test asserting a secret-bearing `http` destination does not render its path segments on the source detail page.
sneak was assigned by clawbot 2026-08-11 14:28:51 +02:00
Author
Collaborator

Correction before you decide: this issue's claim that "the full value stays available in the edit form" is false. There is no target edit route — internal/server/routes.go has only create, toggle and delete, and source_edit.html edits the webhook, not its targets. So option 1 makes the destination URL write-once and unrecoverable from the UI, not merely hidden on one page. Filed as #127, which is what makes option 1 cheap again.

Recommendation unchanged (option 1), with that caveat: the value is a credential, and it is already unreadable-by-design for slack targets.

Implemented speculatively in #126 so you can decide by merging or closing. It is WIP: -prefixed and therefore unmergeable until you rule; strip the prefix to accept. Sibling-path audit in that PR found no second leak: source_logs, sources, source_edit, the SSRF rejection path and /metrics are all clean, and TargetView has no raw config field.

Correction before you decide: this issue's claim that "the full value stays available in the edit form" is **false**. There is no target edit route — `internal/server/routes.go` has only create, toggle and delete, and `source_edit.html` edits the webhook, not its targets. So option 1 makes the destination URL write-once and unrecoverable from the UI, not merely hidden on one page. Filed as https://git.eeqj.de/sneak/webhooker/issues/127, which is what makes option 1 cheap again. Recommendation unchanged (option 1), with that caveat: the value is a credential, and it is already unreadable-by-design for `slack` targets. Implemented speculatively in https://git.eeqj.de/sneak/webhooker/pulls/126 so you can decide by merging or closing. It is `WIP: `-prefixed and therefore unmergeable until you rule; strip the prefix to accept. Sibling-path audit in that PR found no second leak: `source_logs`, `sources`, `source_edit`, the SSRF rejection path and `/metrics` are all clean, and `TargetView` has no raw config field.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/webhooker#115