Skip to content

fix(slack): reject URLs that are not Incoming Webhooks - #6

Open
crstian19 wants to merge 1 commit into
mainfrom
fix/slack-webhook-validation
Open

crstian19 wants to merge 1 commit into
mainfrom
fix/slack-webhook-validation

Conversation

@crstian19

@crstian19 crstian19 commented Aug 20, 2026 •

Copy link
Copy Markdown
Contributor

The bug

A Slack client URL (https://app.slack.com/client/T…/D…) answers a POST with the Slack web app and HTTP 200. post() only checked status_code < 400, so it read that as a successful delivery: the bot logged Notified Slack about thread … without posting anything.

It happened in production. The deployed .env held a link to a DM instead of a webhook, and the bot reported success on every thread. That false positive sent us looking for the problem in Discord for a while.

The fix

Two checks, because either one alone leaves a hole:

  1. The URL must start with https://hooks.slack.com/services/. It is validated once in __init__, so a wrong configuration is one clear error at startup instead of one line per notification.
  2. A 2xx is not enough. An Incoming Webhook answers with the body ok; anything else means the endpoint is not a webhook, however healthy the status code looks.

The first alone would not be enough, because a deleted webhook on the right domain can answer something other than ok. The second alone would not be enough either, because any host answering ok would pass.

README (SLACK_WEBHOOK_URL row and the Slack section) and AGENTS.md describe both checks. The test fixture uses a placeholder client URL (T00000000/D00000000) rather than a real workspace/DM ID, because this repo is public.

Verification

  • pytest with Python 3.11 (as in CI and the Dockerfile): 67 passed (61 on main, 6 new). The new tests cover four non-webhook URL shapes (including the DM link that caused this), a 200 with HTML, and that OK\n counts as delivered.
  • Mutation-checked:
    • dropping the prefix check, accepting any 2xx, removing .lower() or .strip(), and making enabled ignore the validation each fail a named test;
    • the only survivor is an equivalent mutant, because a 2xx without ok falls through to the same False.
  • ruff check and ruff format --check are clean.
  • Not verified here: that the deployed .env has been corrected. Merging deploys straight to production (deploy.yml: push to main → GHCR → docker compose up). After the deploy, check the container logs for SLACK_WEBHOOK_URL is not an Incoming Webhook…. If it shows up, the server's .env still holds the DM link and needs fixing by hand.

A Slack client URL (https://app.slack.com/client/T…/D…) answers a POST with
the web app and HTTP 200. post() only checked `status_code < 400`, so it
read that as a delivered message: the bot logged "Notified Slack about
thread …" while nothing was ever posted.

Two checks, because either one alone still leaves a hole:

- The URL must start with https://hooks.slack.com/services/. Validated once
  in __init__ so a misconfigured value is one loud error at startup instead
  of one line per notification.
- A 2xx is not enough. An Incoming Webhook answers with the body "ok";
  anything else means the endpoint is not a webhook, however healthy the
  status code looks.

Found in production: the deployed .env held a DM link instead of a webhook,
and the bot reported success on every thread.

The README and AGENTS.md describe both checks, and the test fixture uses a
placeholder Slack client URL instead of a real workspace/DM ID (this repo is
public).
@crstian19
crstian19 force-pushed the fix/slack-webhook-validation branch from ed4115f to e19ca22 Compare October 20, 2026 08:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants