Skip to content

Add webhook notification channel to API layer#626

Merged
neSpecc merged 7 commits intomasterfrom
feat/webhook-notifications
Feb 18, 2026
Merged

Add webhook notification channel to API layer#626
neSpecc merged 7 commits intomasterfrom
feat/webhook-notifications

Conversation

@Dobrunia
Copy link
Member

@Dobrunia Dobrunia commented Feb 16, 2026

Summary

  • Register webhook as a supported notification channel in the GraphQL API and RabbitMQ routing so the backend can accept, store, and dispatch webhook notifications

@github-actions
Copy link
Contributor

Please add a PR description 🙂

Respect the reviewers — a description helps others understand the changes and review them faster. Keep it short, clear, and to the point. It also serves as documentation for future reference.

The PR was moved to Draft until a description is added.

@Dobrunia Dobrunia marked this pull request as ready for review February 16, 2026 19:00
@Dobrunia Dobrunia changed the title Enhance notification system with webhook support Add webhook notification channel to API layer Feb 17, 2026
@github-actions
Copy link
Contributor

Please add a PR description 🙂

Respect the reviewers — a description helps others understand the changes and review them faster. Keep it short, clear, and to the point. It also serves as documentation for future reference.

The PR was moved to Draft until a description is added.

@github-actions github-actions bot marked this pull request as draft February 17, 2026 01:43
@github-actions
Copy link
Contributor

Thanks for adding a description — the PR is now marked as Ready for Review.

@github-actions github-actions bot marked this pull request as ready for review February 17, 2026 01:44
…erty to NotificationsChannelsDBScheme interface
… and related tests. Integrate private IP validation into ipValidator module for improved code organization.
@codecov-commenter
Copy link

Codecov Report

❌ Patch coverage is 93.10345% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 41.34%. Comparing base (0f8dad9) to head (38ebc9b).
⚠️ Report is 22 commits behind head on master.

Files with missing lines Patch % Lines
src/utils/webhookEndpointValidator.ts 92.00% 2 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           master     #626       +/-   ##
===========================================
- Coverage   58.30%   41.34%   -16.96%     
===========================================
  Files          19       44       +25     
  Lines         518     2177     +1659     
  Branches       95      464      +369     
===========================================
+ Hits          302      900      +598     
- Misses        216     1210      +994     
- Partials        0       67       +67     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment on lines 4 to 21
Copy link
Member

Choose a reason for hiding this comment

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

move to ipValidator

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds first-class support for a webhook notification channel across the API layer, so clients can configure webhook endpoints via GraphQL and the backend can enqueue webhook deliveries through RabbitMQ.

Changes:

  • Registers webhook as a notification channel in GraphQL types/inputs and DB channel typings.
  • Enqueues webhook notifications via a new RabbitMQ queue/worker path.
  • Introduces SSRF-oriented webhook endpoint validation (protocol/port/hostname/DNS) with new unit tests.

Reviewed changes

Copilot reviewed 11 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
yarn.lock Locks updated dependency graph for @hawk.so/types@^0.5.9.
package.json Bumps version and updates @hawk.so/types to ^0.5.9.
src/types/notification-channels.d.ts Adds webhook to NotificationsChannelsDBScheme.
src/typeDefs/notificationsInput.ts Adds webhook to GraphQL NotificationsChannelsInput.
src/typeDefs/notifications.ts Adds webhook to GraphQL NotificationsChannels.
src/rabbitmq.ts Adds Queues.Webhook and WorkerPaths.Webhook.
src/utils/personalNotifications.ts Enqueues webhook deliveries when the channel is enabled.
src/utils/ipValidator.ts Adds isPrivateIP helper for private/reserved IP detection.
src/utils/webhookEndpointValidator.ts Adds async webhook URL validator with DNS resolution checks.
src/resolvers/userNotifications.ts Validates webhook endpoint when updating user notification channels.
src/resolvers/projectNotifications.ts Validates webhook endpoint when creating/updating project rules.
test/utils/ipValidator.test.ts Unit tests for isPrivateIP.
test/utils/webhookEndpointValidator.test.ts Unit tests for validateWebhookEndpoint.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +55 to +63
const hostname = url.hostname;

if (BLOCKED_HOSTNAMES.some((pattern) => pattern.test(hostname))) {
return `Webhook hostname "${hostname}" is not allowed`;
}

if (isPrivateIP(hostname)) {
return 'Webhook URL points to a private/reserved IP address';
}
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

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

isPrivateIP(hostname) is being applied to any hostname string. This will incorrectly reject valid public domains that merely start with a private-looking prefix (e.g. 10.example.com, 192.168.my-domain.com) because the regexes only check string prefixes, not whether the hostname is an actual IP literal. Consider only running the private-IP check when hostname is an IP address (e.g. via net.isIP on a normalized hostname), and rely on the DNS lookup results for domain names. Adding a test case for a domain like 10.example.com would prevent regressions.

Copilot uses AI. Check for mistakes.
Comment on lines +41 to +45
export function isPrivateIP(ip: string): boolean {
const bare = ip.split('%')[0];

return PRIVATE_IP_PATTERNS.some((pattern) => pattern.test(bare));
}
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

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

isPrivateIP currently treats any string as an IP and runs prefix regexes against it. This makes the function return true for non-IP inputs like 10.example.com, which is inconsistent with the function name/docs and can cause false positives in callers. Consider first validating the normalized input with net.isIP (after stripping any zone id) and returning false when it isn't a valid IPv4/IPv6 literal.

Copilot uses AI. Check for mistakes.
Comment on lines 50 to 56
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

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

Webhook endpoint validation is skipped when input.webhook.endpoint is an empty string because the condition requires it to be truthy. Since GraphQL String! can still be empty, this allows isEnabled: true with an invalid/empty endpoint to bypass validation and be persisted. Consider validating whenever input.webhook?.isEnabled is true (and failing if the endpoint is empty/invalid), rather than gating on truthiness.

Copilot uses AI. Check for mistakes.
Comment on lines 138 to 143
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

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

validateWebhookChannel skips validation when channels.webhook.endpoint is an empty string due to the truthy check. Because the GraphQL input type uses String!, an empty string is still a valid value and would bypass validation while the channel is enabled. Consider validating whenever channels.webhook?.isEnabled is true (and treating empty endpoints as invalid).

Copilot uses AI. Check for mistakes.
…mproved error handling. Update validation logic to await results from channel checks, enhancing overall reliability.
… ALLOWED_PORTS constants from ipValidator module, improving code organization and maintainability.
…o remove endpoint checks when isEnabled is true, streamlining the validation process.
@neSpecc neSpecc merged commit 3d9de27 into master Feb 18, 2026
7 checks passed
@neSpecc neSpecc deleted the feat/webhook-notifications branch February 18, 2026 16:08
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.

3 participants

Comments