n8n
Static analysis for n8n workflow files to detect loops, AI agent vulnerabilities, and credential exposure.
Quick Start
# Export workflow from n8n UI, then scan
inkog scan ./workflowsWhat Inkog Detects
| Finding | Severity | Description |
|---|---|---|
| Workflow Loop | CRITICAL | Split/Merge or Loop node without limits |
| AI Agent Risk | CRITICAL | AI Agent node with unrestricted tools |
| Credential Leak | CRITICAL | API keys exposed in workflow JSON |
| HTTP Request Risk | HIGH | HTTP Request to arbitrary URLs |
| Code Node Risk | CRITICAL | Code nodes with dangerous operations |
Workflow Loops
n8n Loop nodes without iteration limits run forever.
Vulnerable
No maxIterations - loops forever
{
"nodes": [
{
"name": "Loop Over Items",
"type": "n8n-nodes-base.loop",
"parameters": {}
},
{
"name": "HTTP Request",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "https://api.example.com"
}
}
],
"connections": {
"Loop Over Items": {
"main": [[{"node": "HTTP Request"}]]
},
"HTTP Request": {
"main": [[{"node": "Loop Over Items"}]]
}
}
}Secure
Explicit iteration limit and timeout
{
"nodes": [
{
"name": "Loop Over Items",
"type": "n8n-nodes-base.loop",
"parameters": {
"maxIterations": 100
}
},
{
"name": "HTTP Request",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "https://api.example.com",
"timeout": 30000
}
},
{
"name": "Check Limit",
"type": "n8n-nodes-base.if",
"parameters": {
"conditions": {
"number": [{
"value1": "={{$runIndex}}",
"operation": "smaller",
"value2": 100
}]
}
}
}
]
}AI Agent Node Vulnerabilities
AI Agent nodes using LangChain can execute unsafe tools.
Vulnerable
Code tool with eval() - RCE risk
{
"nodes": [
{
"name": "AI Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"parameters": {
"tools": [
{
"type": "@n8n/n8n-nodes-langchain.toolCode",
"parameters": {
"code": "return eval($input)"
}
}
]
}
}
]
}Secure
Safe built-in tools with iteration limit
{
"nodes": [
{
"name": "AI Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"parameters": {
"maxIterations": 10,
"tools": [
{
"type": "@n8n/n8n-nodes-langchain.toolCalculator"
},
{
"type": "@n8n/n8n-nodes-langchain.toolWikipedia"
}
],
"systemMessage": "Only use provided tools. Never execute code."
}
}
]
}Credential Exposure
Workflow exports can contain plaintext credentials.
Vulnerable
API keys hardcoded in workflow
{
"nodes": [
{
"name": "OpenAI",
"type": "@n8n/n8n-nodes-langchain.lmOpenAi",
"parameters": {
"apiKey": "sk-abc123..."
}
},
{
"name": "HTTP Request",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "https://api.stripe.com",
"headers": {
"Authorization": "Bearer sk_live_..."
}
}
}
]
}Secure
Credentials stored in n8n credential store
{
"nodes": [
{
"name": "OpenAI",
"type": "@n8n/n8n-nodes-langchain.lmOpenAi",
"credentials": {
"openAiApi": {
"id": "1",
"name": "OpenAI Account"
}
}
},
{
"name": "HTTP Request",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "https://api.stripe.com"
},
"credentials": {
"httpHeaderAuth": {
"id": "2",
"name": "Stripe API"
}
}
}
]
}HTTP Request to Arbitrary URLs
HTTP nodes accepting dynamic URLs enable SSRF.
Vulnerable
User controls URL - SSRF risk
{
"nodes": [
{
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"parameters": {
"path": "fetch-url"
}
},
{
"name": "HTTP Request",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "={{$json.url}}"
}
}
]
}Secure
URL validation with allowlist
{
"nodes": [
{
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"parameters": {
"path": "fetch-url"
}
},
{
"name": "Validate URL",
"type": "n8n-nodes-base.code",
"parameters": {
"code": "const url = new URL($json.url); const allowed = ['api.example.com']; if (!allowed.includes(url.hostname)) throw new Error('URL not allowed'); return $json;"
}
},
{
"name": "HTTP Request",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "={{$json.url}}",
"timeout": 10000
}
}
]
}Code Node Risks
Code nodes can execute dangerous operations.
Vulnerable
Shell command execution
{
"name": "Code",
"type": "n8n-nodes-base.code",
"parameters": {
"code": "const { execSync } = require('child_process'); return execSync($json.command).toString();"
}
}Secure
Data transformation only
{
"name": "Code",
"type": "n8n-nodes-base.code",
"parameters": {
"code": "// Safe operations only - no require(), no eval(), no exec()
const data = $json.data;
const processed = data.map(item => ({ id: item.id, name: item.name.trim() }));
return processed;"
}
}How to Export Workflows
To scan n8n workflows, export them as JSON:
- Single Workflow: Workflow menu → Download as JSON
- All Workflows: Settings → Export → Download all workflows
- API Export:
# Export via n8n API
curl -X GET "http://localhost:5678/api/v1/workflows" \
-H "X-N8N-API-KEY: your-api-key" \
-o workflows.jsonThen scan:
inkog scan ./workflows.jsonBest Practices
- Set
maxIterationson all Loop nodes - Use credential store - never hardcode API keys
- Avoid Code nodes with
require()oreval() - Validate URLs before HTTP Request nodes
- Limit AI Agent iterations with
maxIterations - Review exports before sharing or committing
CLI Examples
# Scan workflow files
inkog scan ./workflows
# Check for credential leaks
inkog scan . -severity critical
# JSON output for CI
inkog scan ./workflows -output jsonRelated
- Flowise - Similar visual builder
- Resource Exhaustion
- Data Exposure
Last updated on