Skip to main content

n8n-security-testing

Credential exposure detection, OAuth flow validation, API key management testing, and data sanitization verification for n8n workflows. Use when validating n8n workflow security.

Ir para a instalação

Informações da origem

Repositório
natea/ai-news-influencer
Última atividade na origem
4 de janeiro de 2026 às 18:56
Idioma detectado do SKILL.md
inglês
Estrelas
0
Forks
1

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
name
n8n-security-testing
description
Credential exposure detection, OAuth flow validation, API key management testing, and data sanitization verification for n8n workflows. Use when validating n8n workflow security.
category
n8n-testing
priority
critical
tokenEstimate
1100
agents
["n8n-integration-test"]
implementation_status
production
optimization_version
1
last_optimized
2025-12-15T00:00:00.000Z
dependencies
[]
quick_reference_card
true
tags
["n8n","security","credentials","oauth","api-keys","encryption","testing"]
# n8n Security Testing <default_to_action> When testing n8n security: 1. SCAN for credential exposure in workflows 2. VERIFY encryption of sensitive data 3. TEST OAuth token handling 4. CHECK for insecure data transmission 5. VALIDATE input sanitization **Quick Security Checklist:** - No credentials in workflow JSON - No credentials in execution logs - OAuth tokens properly encrypted - API keys not in version control - Webhook authentication enabled - Input data sanitized **Critical Success Factors:** - Scan all workflow exports - Test credential rotation - Verify encryption at rest - Check audit logging </default_to_action> ## Quick Reference Card ### Security Risk Areas | Area | Risk Level | Testing Focus | |------|------------|---------------| | **Credential Storage** | Critical | Encryption, exposure | | **Webhook Security** | High | Authentication, validation | | **Expression Injection** | High | Input sanitization | | **Data Leakage** | Medium | Logging, error messages | | **OAuth Flows** | Medium | Token handling, refresh | ### Credential Types | Type | Exposure Risk | Rotation | |------|---------------|----------| | **API Keys** | High if exposed | Manual | | **OAuth Tokens** | Medium (short-lived) | Automatic | | **Passwords** | Critical | Manual | | **Webhooks** | Medium | Generate new | --- ## Credential Security Testing ### Scan for Exposed Credentials ```typescript // Scan workflow JSON for credential exposure async function scanForExposedCredentials(workflowId: string): Promise<CredentialScanResult> { const workflow = await getWorkflow(workflowId); const workflowJson = JSON.stringify(workflow, null, 2); const sensitivePatterns = [ // API Keys { name: 'Generic API Key', pattern: /api[_-]?key["\s:=]+["']?([a-zA-Z0-9_-]{20,})["']?/gi }, { name: 'AWS Access Key', pattern: /AKIA[0-9A-Z]{16}/g }, { name: 'AWS Secret Key', pattern: /[a-zA-Z0-9/+=]{40}/g }, // Tokens { name: 'Bearer Token', pattern: /bearer\s+[a-zA-Z0-9_-]{20,}/gi }, { name: 'JWT Token', pattern: /eyJ[a-zA-Z0-9_-]*\.eyJ[a-zA-Z0-9_-]*\.[a-zA-Z0-9_-]*/g }, { name: 'Slack Token', pattern: /xox[baprs]-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24}/g }, // Passwords { name: 'Password Field', pattern: /"password":\s*"[^"]+"/gi }, { name: 'Secret Field', pattern: /"secret":\s*"[^"]+"/gi }, // OAuth { name: 'Client Secret', pattern: /client[_-]?secret["\s:=]+["']?([a-zA-Z0-9_-]{20,})["']?/gi }, { name: 'Refresh Token', pattern: /refresh[_-]?token["\s:=]+["']?([a-zA-Z0-9_-]{20,})["']?/gi } ]; const findings: CredentialFinding[] = []; for (const pattern of sensitivePatterns) { const matches = workflowJson.match(pattern.pattern); if (matches) { for (const match of matches) { findings.push({ type: pattern.name, location: findLocationInWorkflow(workflow, match), severity: 'CRITICAL', recommendation: `Remove ${pattern.name} from workflow. Use n8n credentials instead.` }); } } } return { workflowId, scanned: true, findingsCount: findings.length, findings, secure: findings.length === 0 }; } ``` ### Verify Credential Encryption ```typescript // Verify credentials are encrypted at rest async function verifyCredentialEncryption(credentialId: string): Promise<EncryptionResult> { // Get credential metadata (not the actual credential) const credential = await getCredentialMetadata(credentialId); // Check if credential data is encrypted const encryptionChecks = { // Check if stored data looks encrypted (not plain text) isEncrypted: !isPlainText(credential.data), // Check encryption algorithm algorithm: credential.encryptionAlgorithm || 'unknown', // Check key derivation keyDerivation: credential.keyDerivation || 'unknown', // Check if using instance encryption key instanceEncryption: credential.useInstanceKey || false }; return { credentialId, credentialName: credential.name, credentialType: credential.type, encryption: encryptionChecks, secure: encryptionChecks.isEncrypted && encryptionChecks.algorithm !== 'unknown', recommendations: generateEncryptionRecommendations(encryptionChecks) }; } // Check if data appears to be plain text function isPlainText(data: string): boolean { // Plain text credentials often have recognizable patterns const plainTextPatterns = [ /^[a-zA-Z0-9_-]+$/, // Simple alphanumeric /^sk-[a-zA-Z0-9]+$/, // API key format /^Bearer\s/, // Bearer token ]; return plainTextPatterns.some(p => p.test(data)); } ``` ### Test Credential Rotation ```typescript // Test credential rotation process async function testCredentialRotation(credentialId: string): Promise<RotationTestResult> { const credential = await getCredentialMetadata(credentialId); const rotationTests = { // Check if credential has rotation metadata hasRotationSchedule: !!credential.rotationSchedule, lastRotated: credential.lastRotatedAt, rotationDue: isRotationDue(credential), // Test OAuth token refresh oauthRefresh: credential.type.includes('oauth') ? await testOAuthRefresh(credentialId) : null, // Check credential age credentialAge: calculateAge(credential.createdAt), isStale: calculateAge(credential.createdAt) > 90 // 90 days }; return { credentialId, rotationTests, recommendations: generateRotationRecommendations(rotationTests) }; } // Test OAuth token refresh async function testOAuthRefresh(credentialId: string): Promise<OAuthRefreshResult> { try { // Trigger refresh const refreshed = await refreshCredential(credentialId); return { success: true, newExpiry: refreshed.expiresAt, refreshedAt: new Date() }; } catch (error) { return { success: false, error: error.message, recommendation: 'Re-authorize OAuth connection' }; } } ``` --- ## Webhook Security Testing ### Authentication Testing ```typescript // Test webhook authentication enforcement async function testWebhookAuthentication(webhookUrl: string): Promise<WebhookAuthResult> { const authTests = [ // No authentication { name: 'No Auth', headers: {}, expectedStatus: 401 }, // Invalid Basic Auth { name: 'Invalid Basic Auth', headers: { 'Authorization': 'Basic aW52YWxpZDppbnZhbGlk' }, expectedStatus: 401 }, // Invalid Bearer Token { name: 'Invalid Bearer', headers: { 'Authorization': 'Bearer invalid-token-12345' }, expectedStatus: 401 }, // Invalid Header Auth { name: 'Invalid Header Auth', headers: { 'X-API-Key': 'invalid-key' }, expectedStatus: 401 } ]; const results: AuthTestResult[] = []; for (const test of authTests) { const response = await fetch(webhookUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', ...test.headers }, body: '{}' }); results.push({ test: test.name, status: response.status, passed: response.status === test.expectedStatus, actualStatus: response.status, expectedStatus: test.expectedStatus }); } // Check if webhook has ANY auth const noAuthResponse = results.find(r => r.test === 'No Auth'); const webhookHasAuth = noAuthResponse?.status === 401; return { webhookUrl, hasAuthentication: webhookHasAuth, testResults: results, allTestsPassed: results.every(r => r.passed), recommendation: !webhookHasAuth ? 'CRITICAL: Enable authentication on webhook' : null }; } ``` ### Input Validation Testing ```typescript // Test webhook input validation async function testWebhookInputValidation(webhookUrl: string): Promise<InputValidationResult> { const maliciousPayloads = [ // XSS attempts { name: 'XSS Script Tag', payload: { text: '<script>alert("xss")</script>' }, check: 'sanitized' }, { name: 'XSS Event Handler', payload: { text: '<img onerror="alert(1)" src="x">' }, check: 'sanitized' }, // SQL Injection { name: 'SQL Injection', payload: { id: "1; DROP TABLE users; --" }, check: 'escaped' }, // Command Injection { name: 'Command Injection', payload: { filename: '; rm -rf /' }, check: 'rejected' }, // Path Traversal { name: 'Path Traversal', payload: { path: '../../../etc/passwd' }, check: 'rejected' }, // JSON Injection { name: 'JSON Injection', payload: { data: '{"admin": true}' }, check: 'escaped' }, // Oversized payload { name: 'Oversized Payload', payload: { data: 'x'.repeat(10000000) }, // 10MB check: 'rejected' } ]; const results: ValidationTestResult[] = []; for (const test of maliciousPayloads) { try { const response = await fetch(webhookUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(test.payload) }); const responseBody = await response.text(); results.push({ test: test.name, status: response.status, handled: response.status !== 500, // Not a server error sanitized: !responseBody.includes(test.payload.text || test.payload.data), recommendation: response.status === 500 ? `Input not handled safely: ${test.name}` : null }); } catch (error) { results.push({ test: test.name, handled: false, error: error.message }); } } return { webhookUrl, testsRun: maliciousPayloads.length, passed: results.filter(r => r.handled).length, failed: results.filter(r => !r.handled).length, results, secure: results.every(r => r.handled) }; } ``` --- ## Expression Security Testing ### Detect Dangerous Expressions ```typescript // Scan expressions for security vulnerabilities async function scanExpressionsForSecurity(workflowId: string): Promise<ExpressionSecurityResult> { const workflow = await getWorkflow(workflowId); const expressions = extractExpressions(workflow); const dangerousPatterns = [ // Code execution { name: 'eval()', pattern: /eval\s*\(/g, severity: 'CRITICAL' }, { name: 'Function()', pattern: /new\s+Function\s*\(/g, severity: 'CRITICAL' }, { name: 'setTimeout string', pattern: /setTimeout\s*\(\s*["'`]/g, severity: 'HIGH' }, { name: 'setInterval string', pattern: /setInterval\s*\(\s*["'`]/g, severity: 'HIGH' }, // File system access { name: 'require()', pattern: /require\s*\(/g, severity: 'HIGH' }, { name: 'import()', pattern: /import\s*\(/g, severity: 'HIGH' },
Ver no GitHub
Este SKILL.md e muito grande, entao o SkillsMP mostra aqui apenas a primeira secao. Ver no GitHub