Skip to content

Add Flowtriq notification provider#4239

Open
jacob-masse wants to merge 2 commits into
dgtlmoon:masterfrom
jacob-masse:add-flowtriq-notification-provider
Open

Add Flowtriq notification provider#4239
jacob-masse wants to merge 2 commits into
dgtlmoon:masterfrom
jacob-masse:add-flowtriq-notification-provider

Conversation

@jacob-masse

Copy link
Copy Markdown

Summary

Adds Flowtriq as a native notification provider via a custom Apprise plugin. Flowtriq is a DDoS detection and traffic analytics platform -- correlating website changes/outages detected by changedetection.io with DDoS attack data is a natural fit.

Usage:

flowtriq://app.flowtriq.com/api/v1/webhooks/changedetection
flowtriq://app.flowtriq.com/api/v1/webhooks/changedetection?key=your-api-key

What this does:

  • Registers a flowtriq:// URL scheme as a custom Apprise notification plugin
  • POSTs a structured JSON payload (source, watch_url, title, body, status) to the configured Flowtriq webhook endpoint
  • Supports optional API key authentication via the X-API-Key header (passed as ?key= in the URL)
  • Follows the existing pattern established by the custom Discord plugin

Files changed:

  • changedetectionio/notification/apprise_plugin/flowtriq.py -- the plugin (NotifyFlowtriq class extending NotifyBase)
  • changedetectionio/notification/handler.py -- registers the plugin with Apprise's plugin manager
  • changedetectionio/forms.py -- imports the plugin so URL validation recognizes flowtriq://
  • changedetectionio/blueprint/ui/notification.py -- imports the plugin for test notification support
  • changedetectionio/tests/apprise/test_apprise_flowtriq.py -- unit tests covering URL parsing, send success/failure, payload structure, and API key handling

Test plan

  • Verify flowtriq:// URLs are accepted in the notification URL field without validation errors
  • Verify test notification sends the correct JSON payload structure to the webhook endpoint
  • Verify API key is included as X-API-Key header when ?key= is provided
  • Verify notifications work without an API key
  • Verify connection failures are handled gracefully (returns False, no crash)
  • Run pytest changedetectionio/tests/apprise/test_apprise_flowtriq.py

Adds a custom Apprise plugin for Flowtriq (flowtriq://) that sends
structured JSON webhook payloads for correlating website changes with
DDoS attack data. Supports optional API key authentication via the
X-API-Key header.

Usage: flowtriq://app.flowtriq.com/api/v1/webhooks/changedetection?key=your-key
@dgtlmoon

Copy link
Copy Markdown
Owner

Heya - thanks, but for now probably i wont ship this inside of changedetection, i'll check for some pluggy architecture for writing notification plugins and get back to you, then you could ship it in your own package

@jacob-masse

Copy link
Copy Markdown
Author

Heya - thanks, but for now probably i wont ship this inside of changedetection, i'll check for some pluggy architecture for writing notification plugins and get back to you, then you could ship it in your own package

No worries and thank you! Let me know what you find and I can adapt this PR :)

@AmirF194 AmirF194 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, clean addition. The plugin is a well-formed NotifyBase subclass and the wiring is right: registering in handler.py plus the noqa: F401 imports in forms.py and blueprint/ui/notification.py is what makes flowtriq:// validate in the URL field and work in the test-notification path. I confirmed a registered flowtriq:// URL instantiates and sends through apprise. Failure handling is graceful (RequestException caught, logged, returns False), the X-API-Key header is only set when a key is present, and there are real unit tests that would fail at collection without the new module. I ran all 6 against apprise 1.9.9 and they pass.

A couple of things worth a look before merge:

  1. Egress SSRF. The requests.post in send() goes to a user-controlled host with no private-address check, so flowtriq://169.254.169.254/... or flowtriq://localhost/... are accepted and posted to. The project already guards notification egress at custom_handlers.py:204-210 via is_url_private_or_parser_confused (GHSA-rph4-96w6-q594), gated by ALLOW_IANA_RESTRICTED_ADDRESSES. These URLs are usually operator-set so this is defense in depth, but since the rest of the notification stack enforces it, reusing that helper here (and honoring self.verify_certificate) would keep the posture consistent.

  2. The watch_url field in the payload is set to title. With the default title template ChangeDetection.io Notification - {{watch_url}}, that field carries the whole rendered title string rather than the bare monitored URL, and it duplicates title. Since the point is correlating by URL, it might be worth either documenting that users should set a bare {{watch_url}} title/body, or dropping the redundant field. apprise's send() does not receive the watch URL directly, so those are the honest options.

Optional: in url(), url.rstrip('?key=') strips any trailing characters in the set {?, k, e, y, =}, not the literal suffix, so /api/webhook-key becomes /api/webhook-. Live sends are unaffected since the raw URL is stored and webhook_url is built in __init__, but removesuffix('?key=') (or only appending ?key= when a key is set) would be correct.

One CI note: all checks are green, but I do not think any job actually runs tests/apprise/. unit-tests runs tests/unit/ tests/llm/ and basic-tests runs pytest tests/test_*.py (non-recursive) plus named files, so the new tests are not exercised in CI (the two existing tests/apprise/ files look un-run too, so this predates the PR). I ran them locally and they pass. Adding tests/apprise/ to a CI job would let them gate future changes.

…ield

- Add egress SSRF guard via is_url_private_or_parser_confused, honoring
  ALLOW_IANA_RESTRICTED_ADDRESSES, consistent with custom_handlers.py
- Honor self.verify_certificate in requests.post
- Remove redundant watch_url field from payload (duplicated title)
- Fix url() rstrip bug: build URL conditionally instead of stripping
  a character set that could eat path suffixes
- Add tests for SSRF blocking and url() without API key
@jacob-masse

Copy link
Copy Markdown
Author

@AmirF194 Thanks for the thorough review. All three points addressed in 41aa7e7:

  1. SSRFsend() now calls is_url_private_or_parser_confused() and honors ALLOW_IANA_RESTRICTED_ADDRESSES, matching the guard in custom_handlers.py. Also added verify=self.verify_certificate to the requests.post call.

  2. watch_url field — Removed it. It was just duplicating title with no independent value.

  3. rstrip bug — Replaced with conditional ?key= append so the suffix is only added when an API key is actually set. No character-set stripping.

Added two new tests covering SSRF blocking on private addresses and url() output without an API key.

Re: the CI note about tests/apprise/ not being exercised — agreed that predates this PR. Happy to add it to a CI job if that's wanted, but leaving that to maintainer preference since it touches workflow config.

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.

4 participants