flowengine-n8n-workflow-builder
Version:
Build n8n workflows from text using AI. Connect to Claude, Cursor, or any LLM to generate and validate n8n workflows with expert knowledge and intelligent auto-fixing. Built by FlowEngine. Now with real node parameter schemas from n8n packages!
157 lines • 6.7 kB
JavaScript
/**
* Security Scanner - Vulnerability Detection
*
* Scan workflows for security issues like credential leaks,
* unsafe code execution, and potential vulnerabilities.
*/
/**
* Scan workflow for security issues
*/
export function scanSecurity(workflow) {
const issues = [];
workflow.nodes.forEach(node => {
// Check for hardcoded credentials
const paramStr = JSON.stringify(node.parameters || {});
if (paramStr.match(/api[_-]?key['"]?\s*:\s*['"]/i)) {
issues.push({
severity: 'critical',
type: 'credential-leak',
message: 'Possible hardcoded API key detected',
node: node.name,
recommendation: 'Use n8n credential system instead of hardcoding keys',
});
}
if (paramStr.match(/password['"]?\s*:\s*['"]/i)) {
issues.push({
severity: 'critical',
type: 'credential-leak',
message: 'Possible hardcoded password detected',
node: node.name,
recommendation: 'Use n8n credential system for passwords',
});
}
if (paramStr.match(/token['"]?\s*:\s*['"]/i)) {
issues.push({
severity: 'high',
type: 'credential-leak',
message: 'Possible hardcoded token detected',
node: node.name,
recommendation: 'Store tokens in n8n credentials',
});
}
// Check for unsafe code execution
if (node.type === 'n8n-nodes-base.code') {
const code = node.parameters?.jsCode;
if (typeof code === 'string' && code.includes('eval(')) {
issues.push({
severity: 'critical',
type: 'unsafe-code',
message: 'Use of eval() detected - potential code injection',
node: node.name,
recommendation: 'Avoid eval() - use safe alternatives',
});
}
if (typeof code === 'string' && code.includes('Function(')) {
issues.push({
severity: 'high',
type: 'unsafe-code',
message: 'Use of Function() constructor - potential code injection',
node: node.name,
recommendation: 'Avoid dynamic code generation',
});
}
if (typeof code === 'string' && (code.includes('child_process') || code.includes('exec'))) {
issues.push({
severity: 'critical',
type: 'unsafe-code',
message: 'Execution of system commands detected',
node: node.name,
recommendation: 'Avoid system command execution - use n8n nodes instead',
});
}
}
// Check for HTTP nodes without authentication
if (node.type === 'n8n-nodes-base.httpRequest') {
const auth = node.parameters?.authentication;
const url = node.parameters?.url;
if (!auth || auth === 'none') {
if (typeof url === 'string' && (url.includes('api') || url.includes('webhook'))) {
issues.push({
severity: 'medium',
type: 'missing-auth',
message: 'HTTP request to API endpoint without authentication',
node: node.name,
recommendation: 'Add authentication to protect API calls',
});
}
}
// Check for HTTP instead of HTTPS
if (typeof url === 'string' && url.startsWith('http://') && !url.includes('localhost')) {
issues.push({
severity: 'medium',
type: 'insecure-protocol',
message: 'Using HTTP instead of HTTPS',
node: node.name,
recommendation: 'Use HTTPS for secure communication',
});
}
}
// Check for webhook nodes without validation
if (node.type === 'n8n-nodes-base.webhook') {
const options = node.parameters?.options;
const hasValidation = options?.validation;
if (!hasValidation) {
issues.push({
severity: 'high',
type: 'missing-validation',
message: 'Webhook without input validation',
node: node.name,
recommendation: 'Add validation to prevent malicious input',
});
}
}
// Check for SQL injection vulnerabilities
if (node.type.includes('postgres') || node.type.includes('mysql')) {
const query = node.parameters?.query;
if (typeof query === 'string' && (query.includes('${') || query.includes('${{'))) {
issues.push({
severity: 'critical',
type: 'sql-injection',
message: 'Potential SQL injection - using string interpolation in query',
node: node.name,
recommendation: 'Use parameterized queries instead',
});
}
}
});
return issues.sort((a, b) => {
const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
return severityOrder[a.severity] - severityOrder[b.severity];
});
}
/**
* Check if workflow handles sensitive data
*/
export function detectSensitiveData(workflow) {
const sensitiveTypes = [];
workflow.nodes.forEach(node => {
const paramStr = JSON.stringify(node.parameters || {}).toLowerCase();
if (paramStr.includes('email') || node.type.includes('email') || node.type.includes('gmail')) {
sensitiveTypes.push('Email addresses');
}
if (paramStr.includes('phone') || paramStr.includes('mobile')) {
sensitiveTypes.push('Phone numbers');
}
if (paramStr.includes('credit') || paramStr.includes('card') || paramStr.includes('payment')) {
sensitiveTypes.push('Payment information');
}
if (paramStr.includes('ssn') || paramStr.includes('social security')) {
sensitiveTypes.push('SSN/Government IDs');
}
if (paramStr.includes('health') || paramStr.includes('medical')) {
sensitiveTypes.push('Health/Medical data');
}
});
return [...new Set(sensitiveTypes)];
}
//# sourceMappingURL=security.js.map