n8n-impl-webhooks — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited n8n-impl-webhooks (Agent Skill) and scored it 45/100 (orange). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 1 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
A base64 string of 128+ characters appears in a documentation file. Encoded prompt injection hides the hostile instruction in base64 — invisible to keyword filters — and relies on the agent's ability to decode it at runtime. There is no normal authoring reason to embed a multi-hundred-byte base64 blob in skill docs.
*.sig, SIGNATURES) outside the documentation.Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
| Aspect | Details |
|---|---|
| Trigger node | Webhook |
| HTTP methods | GET, POST, PUT, PATCH, DELETE, HEAD |
| Test URL | <base>/webhook-test/<path> |
| Production URL | <base>/webhook/<path> |
| Response modes | 4 (Immediately, Last Node, Respond to Webhook, Streaming) |
| Auth methods | 4 (None, Basic Auth, Header Auth, JWT Auth) |
| Payload limit | 16MB default (N8N_PAYLOAD_SIZE_MAX) |
| Response node | Respond to Webhook (8 response types) |
ALWAYS understand the two-URL system before deploying webhooks:
<base>/webhook-test/<path>N8N_ENDPOINT_WEBHOOK_TEST (default: webhook-test)<base>/webhook/<path>N8N_ENDPOINT_WEBHOOK (default: webhook)ALWAYS set WEBHOOK_URL when n8n is behind a reverse proxy:
WEBHOOK_URL=https://n8n.example.com/Without this, n8n generates URLs using its internal hostname, which external services cannot reach.
Need to respond to the caller?
|
+-- No --> "Immediately" (returns 200 + "Workflow got started")
|
+-- Yes --> Need custom response logic?
|
+-- No --> "When Last Node Finishes" (returns last node's data)
| |
| +-- Response Data options:
| - All Entries: array of all items
| - First Entry JSON: single JSON object
| - First Entry Binary: binary file download
| - No Response Body: empty 200
|
+-- Yes --> Need streaming?
|
+-- Yes --> "Streaming" (real-time data from streaming-capable nodes)
|
+-- No --> "Using 'Respond to Webhook' Node"
(full control: JSON, text, binary, JWT, redirect, etc.)Who calls this webhook?
|
+-- Internal/trusted service --> None (use network-level security instead)
|
+-- External service with API key --> Header Auth
| (custom header name + value)
|
+-- External service with credentials --> Basic Auth
| (username + password)
|
+-- Service requiring token validation --> JWT Auth
(validate incoming JWTs, extract claims)The Path field defines the URL endpoint. ALWAYS use lowercase, hyphenated paths:
# Static paths
my-webhook
orders/incoming
api/v1/notifications
# Dynamic path parameters (use :param syntax)
orders/:orderId
users/:userId/events
:tenantId/webhooks/:eventTypeAccess dynamic parameters in subsequent nodes:
// In expressions
{{ $json.params.orderId }}
// In Code node
const orderId = $input.first().json.params.orderId;| Method | Use When |
|---|---|
| POST | Receiving data submissions, form data, JSON payloads |
| GET | Health checks, status queries, simple triggers |
| PUT | Full resource updates from external systems |
| PATCH | Partial resource updates |
| DELETE | Deletion notifications |
| HEAD | Availability checks (no body returned) |
ALWAYS use POST for webhooks receiving payload data. NEVER use GET for sensitive data (parameters appear in URL/logs).
| Option | When to Use |
|---|---|
| Binary Data | ALWAYS enable when receiving file uploads |
| IP Whitelist | ALWAYS use when caller IPs are known (comma-separated) |
| CORS | Set specific domains; use * only for public APIs |
| Ignore Bots | Enable for public-facing webhooks |
| Raw Body | Enable when receiving XML or non-JSON payloads |
| Response Headers | Add custom headers (Content-Type, Cache-Control, etc.) |
| Response Code | Override default status code (e.g., 201 for created) |
| Type | Use Case |
|---|---|
| All Incoming Items | Return processed data array |
| First Incoming Item | Return single processed item |
| JSON | Return custom-crafted JSON response |
| Text | Return plain text or HTML |
| Binary File | Return file download |
| JWT Token | Return signed JWT for authentication flows |
| Redirect | Send caller to another URL (302) |
| No Data | Acknowledge with empty body |
<iframe> with sandbox (NEVER rely on JS access to parent window)| Option | Description |
|---|---|
| Response Code | Custom HTTP status code |
| Response Headers | Custom headers (e.g., Content-Type: application/xml) |
| Put Response in Field | Rename the response data field |
| Enable Streaming | Enable for streaming-configured triggers |
1. Create workflow with Webhook node
2. Configure path, method, auth, response mode
3. Test: Click "Listen for Test Event" --> test URL active
4. Deploy: Activate workflow --> production URL active
5. Deactivate workflow --> production URL stops respondingALWAYS verify webhook registration after activation:
curl -I https://n8n.example.com/webhook/<path>
# Expected: HTTP 200 (or auth challenge)
# If 404: workflow is not active or path is wrong| Variable | Default | Purpose |
|---|---|---|
WEBHOOK_URL | — | Public URL for reverse proxy setups |
N8N_ENDPOINT_WEBHOOK | webhook | Production path prefix |
N8N_ENDPOINT_WEBHOOK_TEST | webhook-test | Test path prefix |
N8N_ENDPOINT_WEBHOOK_WAIT | webhook-waiting | Waiting webhook path prefix |
N8N_PAYLOAD_SIZE_MAX | 16MB | Maximum request payload size |
N8N_FORMDATA_FILE_SIZE_MAX | — | Maximum form data file size |
N8N_DISABLE_PRODUCTION_MAIN_PROCESS | false | Offload webhooks to separate process |
ALWAYS enable the Binary Data option when receiving files:
$binary// Access uploaded file in Code node
const fileBuffer = await $input.first().binary.file.data;
const fileName = $input.first().binary.file.fileName;
const mimeType = $input.first().binary.file.mimeType;* (all origins allowed)https://app.example.com,https://admin.example.comIn queue mode (multi-instance deployments):
WEBHOOK_URL to point to the main instancen8n webhook command)N8N_DISABLE_PRODUCTION_MAIN_PROCESS=true when using a separate webhook processor~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.