Skip to content

feat(webhook-triggers-app): address security concerns - #62

Open
alex-petrunin wants to merge 3 commits into
JetBrains:mainfrom
alex-petrunin:JT-95028-Webhook-Triggers-App-single-shared-token
Open

feat(webhook-triggers-app): address security concerns#62
alex-petrunin wants to merge 3 commits into
JetBrains:mainfrom
alex-petrunin:JT-95028-Webhook-Triggers-App-single-shared-token

Conversation

@alex-petrunin

@alex-petrunin alex-petrunin commented Apr 20, 2026

Copy link
Copy Markdown
Collaborator

Each webhook URL can now carry its own independent secret using an inline format in the existing textarea fields:

https://n8n.example.com/webhook token_a
https://slack.com/services/xxxxxxx token_b

Previously a single shared webhookToken was sent to every endpoint. A compromise at any one recipient exposed all others.

Changes:

  • New url+token line format: split on first whitespace, token is optional
  • parseWebhookEntries() is the new core parse primitive; parseWebhookUrls delegates to it (backward-compatible wrapper)
  • getWebhookEntries() is the new core combine/dedup function; getWebhookUrls delegates to it (backward-compatible wrapper)
  • sendWebhooks() resolves token per-entry: inline token takes priority, falls back to global webhookToken, skips the entry if neither is set
  • Runtime deprecation warning fires when 2+ URLs share the global fallback
  • webhookToken made optional in settings.json (existing deployments continue to work unchanged via fallback)
  • All textarea descriptions updated with inline-token format docs

Each webhook URL can now carry its own independent secret using an
inline format in the existing textarea fields:

  https://n8n.example.com/webhook  token_a
  https://slack.com/services/xxx   token_b

Previously a single shared webhookToken was sent to every endpoint.
A compromise at any one recipient exposed all others.

Changes:
- New url+token line format: split on first whitespace, token is optional
- parseWebhookEntries() is the new core parse primitive; parseWebhookUrls
  delegates to it (backward-compatible wrapper)
- getWebhookEntries() is the new core combine/dedup function; getWebhookUrls
  delegates to it (backward-compatible wrapper)
- sendWebhooks() resolves token per-entry: inline token takes priority,
  falls back to global webhookToken, skips the entry if neither is set
- Runtime deprecation warning fires when 2+ URLs share the global fallback
- webhookToken made optional in settings.json (existing deployments
  continue to work unchanged via fallback)
- All textarea descriptions updated with inline-token format docs
…okens and enforce minimum length in settings

- Updated `workflow-http.js` to log security warnings for inline tokens below the recommended length.
- Added `minLength` constraint (32) to `webhookToken` in settings schema.
@alex-petrunin
alex-petrunin force-pushed the JT-95028-Webhook-Triggers-App-single-shared-token branch from a1390fa to 7af380f Compare May 4, 2026 13:41
@alex-petrunin alex-petrunin changed the title JT-95028 Webhook Triggers App: single shared token sent to all webhook endpoints Webhook Triggers App: single shared token sent to all webhook endpoints May 4, 2026
@alex-petrunin alex-petrunin changed the title Webhook Triggers App: single shared token sent to all webhook endpoints feat(webhook-triggers-app): single shared token sent to all webhook endpoints May 4, 2026
@alex-petrunin alex-petrunin changed the title feat(webhook-triggers-app): single shared token sent to all webhook endpoints feat(webhook-triggers-app): address security concerns May 6, 2026

Copilot AI 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.

Pull request overview

This PR updates the webhook-triggers workflow utilities to support per-endpoint secrets by allowing an optional inline token next to each webhook URL, reducing the blast radius of a single recipient compromise while preserving backward compatibility via a global fallback token.

Changes:

  • Added a new parsing primitive (parseWebhookEntries) and a new combine/dedup primitive (getWebhookEntries), with existing URL-only helpers delegating to them.
  • Updated sendWebhooks() to resolve an effective token per entry (inline token first, otherwise global fallback), skipping entries with no token and emitting a deprecation warning when multiple URLs share the global fallback.
  • Updated project settings schema/docs and expanded Vitest coverage for parsing, deduping, and token dispatch behavior.

Reviewed changes

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

File Description
packages/webhook-triggers-app/src/workflows/workflow-http.js Adds entry parsing + dedup helpers and updates dispatch to use per-entry tokens with fallback + warnings.
packages/webhook-triggers-app/src/workflows/workflow-core.js Re-exports the new helpers from the core module.
packages/webhook-triggers-app/src/workflows/tests/workflow-http.test.js Adds tests for entry parsing, dedup behavior, and token selection/dispatch.
packages/webhook-triggers-app/src/settings.json Makes webhookToken optional, enforces minLength, and updates UI descriptions to document inline token format.

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

Comment on lines +177 to +181
console.log('[webhooks] Sending webhooks to ' + allEntries.length + ' URL(s)');

var fallbackCount = 0;
allEntries.forEach(function(entry) {
var effectiveToken = entry.token != null ? entry.token : globalToken;
Comment on lines 8 to 15
"webhookToken": {
"title": "Webhook token",
"description": "The webhook token is a shared secret included in requests to all webhook URLs configured in this project. Only add endpoints you trust. Rotate this token immediately if any recipient is decommissioned or a compromise is suspected. Must be at least 32 characters long.",
"title": "Default webhook token",
"description": "Used for any webhook URL that does not have its own token. If multiple URLs rely on this default token, they all use the same secret, so if one is compromised, the others are too. For better security, add a token to each URL instead, for example: https://example.com mytoken",
"type": "string",
"format": "secret",
"x-scope": "PROJECT"
"x-scope": "PROJECT",
"minLength": 32
},
Comment on lines 24 to 29
"webhooksOnIssueCreated": {
"title": "Issue created",
"description": "Triggered when a new issue is created",
"description": "Triggered when a new issue is created. Enter one URL per line or separate multiple URLs with commas. To use a token for a specific webhook, add it after the URL, separated by a space. For example: https://example.com/hook mytoken",
"type": "string",
"format": "textarea",
"x-scope": "PROJECT"
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