agentic-qe 2.5.5 → 2.5.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/.claude/agents/n8n/n8n-base-agent.md +376 -0
  2. package/.claude/agents/n8n/n8n-bdd-scenario-tester.md +613 -0
  3. package/.claude/agents/n8n/n8n-chaos-tester.md +654 -0
  4. package/.claude/agents/n8n/n8n-ci-orchestrator.md +850 -0
  5. package/.claude/agents/n8n/n8n-compliance-validator.md +685 -0
  6. package/.claude/agents/n8n/n8n-expression-validator.md +560 -0
  7. package/.claude/agents/n8n/n8n-integration-test.md +602 -0
  8. package/.claude/agents/n8n/n8n-monitoring-validator.md +589 -0
  9. package/.claude/agents/n8n/n8n-node-validator.md +455 -0
  10. package/.claude/agents/n8n/n8n-performance-tester.md +630 -0
  11. package/.claude/agents/n8n/n8n-security-auditor.md +786 -0
  12. package/.claude/agents/n8n/n8n-trigger-test.md +500 -0
  13. package/.claude/agents/n8n/n8n-unit-tester.md +633 -0
  14. package/.claude/agents/n8n/n8n-version-comparator.md +567 -0
  15. package/.claude/agents/n8n/n8n-workflow-executor.md +392 -0
  16. package/.claude/skills/n8n-expression-testing/SKILL.md +434 -0
  17. package/.claude/skills/n8n-integration-testing-patterns/SKILL.md +540 -0
  18. package/.claude/skills/n8n-security-testing/SKILL.md +599 -0
  19. package/.claude/skills/n8n-trigger-testing-strategies/SKILL.md +541 -0
  20. package/.claude/skills/n8n-workflow-testing-fundamentals/SKILL.md +447 -0
  21. package/CHANGELOG.md +111 -0
  22. package/README.md +7 -4
  23. package/dist/adapters/MemoryStoreAdapter.d.ts +75 -123
  24. package/dist/adapters/MemoryStoreAdapter.d.ts.map +1 -1
  25. package/dist/adapters/MemoryStoreAdapter.js +204 -219
  26. package/dist/adapters/MemoryStoreAdapter.js.map +1 -1
  27. package/dist/agents/AccessibilityAllyAgent.d.ts.map +1 -1
  28. package/dist/agents/AccessibilityAllyAgent.js +17 -1
  29. package/dist/agents/AccessibilityAllyAgent.js.map +1 -1
  30. package/dist/agents/BaseAgent.d.ts +18 -250
  31. package/dist/agents/BaseAgent.d.ts.map +1 -1
  32. package/dist/agents/BaseAgent.js +122 -520
  33. package/dist/agents/BaseAgent.js.map +1 -1
  34. package/dist/agents/n8n/N8nAPIClient.d.ts +121 -0
  35. package/dist/agents/n8n/N8nAPIClient.d.ts.map +1 -0
  36. package/dist/agents/n8n/N8nAPIClient.js +367 -0
  37. package/dist/agents/n8n/N8nAPIClient.js.map +1 -0
  38. package/dist/agents/n8n/N8nAuditPersistence.d.ts +120 -0
  39. package/dist/agents/n8n/N8nAuditPersistence.d.ts.map +1 -0
  40. package/dist/agents/n8n/N8nAuditPersistence.js +473 -0
  41. package/dist/agents/n8n/N8nAuditPersistence.js.map +1 -0
  42. package/dist/agents/n8n/N8nBDDScenarioTesterAgent.d.ts +159 -0
  43. package/dist/agents/n8n/N8nBDDScenarioTesterAgent.d.ts.map +1 -0
  44. package/dist/agents/n8n/N8nBDDScenarioTesterAgent.js +697 -0
  45. package/dist/agents/n8n/N8nBDDScenarioTesterAgent.js.map +1 -0
  46. package/dist/agents/n8n/N8nBaseAgent.d.ts +126 -0
  47. package/dist/agents/n8n/N8nBaseAgent.d.ts.map +1 -0
  48. package/dist/agents/n8n/N8nBaseAgent.js +446 -0
  49. package/dist/agents/n8n/N8nBaseAgent.js.map +1 -0
  50. package/dist/agents/n8n/N8nCIOrchestratorAgent.d.ts +164 -0
  51. package/dist/agents/n8n/N8nCIOrchestratorAgent.d.ts.map +1 -0
  52. package/dist/agents/n8n/N8nCIOrchestratorAgent.js +610 -0
  53. package/dist/agents/n8n/N8nCIOrchestratorAgent.js.map +1 -0
  54. package/dist/agents/n8n/N8nChaosTesterAgent.d.ts +205 -0
  55. package/dist/agents/n8n/N8nChaosTesterAgent.d.ts.map +1 -0
  56. package/dist/agents/n8n/N8nChaosTesterAgent.js +729 -0
  57. package/dist/agents/n8n/N8nChaosTesterAgent.js.map +1 -0
  58. package/dist/agents/n8n/N8nComplianceValidatorAgent.d.ts +228 -0
  59. package/dist/agents/n8n/N8nComplianceValidatorAgent.d.ts.map +1 -0
  60. package/dist/agents/n8n/N8nComplianceValidatorAgent.js +986 -0
  61. package/dist/agents/n8n/N8nComplianceValidatorAgent.js.map +1 -0
  62. package/dist/agents/n8n/N8nContractTesterAgent.d.ts +213 -0
  63. package/dist/agents/n8n/N8nContractTesterAgent.d.ts.map +1 -0
  64. package/dist/agents/n8n/N8nContractTesterAgent.js +989 -0
  65. package/dist/agents/n8n/N8nContractTesterAgent.js.map +1 -0
  66. package/dist/agents/n8n/N8nExpressionValidatorAgent.d.ts +99 -0
  67. package/dist/agents/n8n/N8nExpressionValidatorAgent.d.ts.map +1 -0
  68. package/dist/agents/n8n/N8nExpressionValidatorAgent.js +632 -0
  69. package/dist/agents/n8n/N8nExpressionValidatorAgent.js.map +1 -0
  70. package/dist/agents/n8n/N8nFailureModeTesterAgent.d.ts +238 -0
  71. package/dist/agents/n8n/N8nFailureModeTesterAgent.d.ts.map +1 -0
  72. package/dist/agents/n8n/N8nFailureModeTesterAgent.js +956 -0
  73. package/dist/agents/n8n/N8nFailureModeTesterAgent.js.map +1 -0
  74. package/dist/agents/n8n/N8nIdempotencyTesterAgent.d.ts +242 -0
  75. package/dist/agents/n8n/N8nIdempotencyTesterAgent.d.ts.map +1 -0
  76. package/dist/agents/n8n/N8nIdempotencyTesterAgent.js +992 -0
  77. package/dist/agents/n8n/N8nIdempotencyTesterAgent.js.map +1 -0
  78. package/dist/agents/n8n/N8nIntegrationTestAgent.d.ts +104 -0
  79. package/dist/agents/n8n/N8nIntegrationTestAgent.d.ts.map +1 -0
  80. package/dist/agents/n8n/N8nIntegrationTestAgent.js +653 -0
  81. package/dist/agents/n8n/N8nIntegrationTestAgent.js.map +1 -0
  82. package/dist/agents/n8n/N8nMonitoringValidatorAgent.d.ts +210 -0
  83. package/dist/agents/n8n/N8nMonitoringValidatorAgent.d.ts.map +1 -0
  84. package/dist/agents/n8n/N8nMonitoringValidatorAgent.js +669 -0
  85. package/dist/agents/n8n/N8nMonitoringValidatorAgent.js.map +1 -0
  86. package/dist/agents/n8n/N8nNodeValidatorAgent.d.ts +142 -0
  87. package/dist/agents/n8n/N8nNodeValidatorAgent.d.ts.map +1 -0
  88. package/dist/agents/n8n/N8nNodeValidatorAgent.js +1090 -0
  89. package/dist/agents/n8n/N8nNodeValidatorAgent.js.map +1 -0
  90. package/dist/agents/n8n/N8nPerformanceTesterAgent.d.ts +198 -0
  91. package/dist/agents/n8n/N8nPerformanceTesterAgent.d.ts.map +1 -0
  92. package/dist/agents/n8n/N8nPerformanceTesterAgent.js +653 -0
  93. package/dist/agents/n8n/N8nPerformanceTesterAgent.js.map +1 -0
  94. package/dist/agents/n8n/N8nReplayabilityTesterAgent.d.ts +245 -0
  95. package/dist/agents/n8n/N8nReplayabilityTesterAgent.d.ts.map +1 -0
  96. package/dist/agents/n8n/N8nReplayabilityTesterAgent.js +952 -0
  97. package/dist/agents/n8n/N8nReplayabilityTesterAgent.js.map +1 -0
  98. package/dist/agents/n8n/N8nSecretsHygieneAuditorAgent.d.ts +325 -0
  99. package/dist/agents/n8n/N8nSecretsHygieneAuditorAgent.d.ts.map +1 -0
  100. package/dist/agents/n8n/N8nSecretsHygieneAuditorAgent.js +1187 -0
  101. package/dist/agents/n8n/N8nSecretsHygieneAuditorAgent.js.map +1 -0
  102. package/dist/agents/n8n/N8nSecurityAuditorAgent.d.ts +91 -0
  103. package/dist/agents/n8n/N8nSecurityAuditorAgent.d.ts.map +1 -0
  104. package/dist/agents/n8n/N8nSecurityAuditorAgent.js +825 -0
  105. package/dist/agents/n8n/N8nSecurityAuditorAgent.js.map +1 -0
  106. package/dist/agents/n8n/N8nTestHarness.d.ts +131 -0
  107. package/dist/agents/n8n/N8nTestHarness.d.ts.map +1 -0
  108. package/dist/agents/n8n/N8nTestHarness.js +456 -0
  109. package/dist/agents/n8n/N8nTestHarness.js.map +1 -0
  110. package/dist/agents/n8n/N8nTriggerTestAgent.d.ts +119 -0
  111. package/dist/agents/n8n/N8nTriggerTestAgent.d.ts.map +1 -0
  112. package/dist/agents/n8n/N8nTriggerTestAgent.js +652 -0
  113. package/dist/agents/n8n/N8nTriggerTestAgent.js.map +1 -0
  114. package/dist/agents/n8n/N8nUnitTesterAgent.d.ts +130 -0
  115. package/dist/agents/n8n/N8nUnitTesterAgent.d.ts.map +1 -0
  116. package/dist/agents/n8n/N8nUnitTesterAgent.js +522 -0
  117. package/dist/agents/n8n/N8nUnitTesterAgent.js.map +1 -0
  118. package/dist/agents/n8n/N8nVersionComparatorAgent.d.ts +201 -0
  119. package/dist/agents/n8n/N8nVersionComparatorAgent.d.ts.map +1 -0
  120. package/dist/agents/n8n/N8nVersionComparatorAgent.js +645 -0
  121. package/dist/agents/n8n/N8nVersionComparatorAgent.js.map +1 -0
  122. package/dist/agents/n8n/N8nWorkflowExecutorAgent.d.ts +120 -0
  123. package/dist/agents/n8n/N8nWorkflowExecutorAgent.d.ts.map +1 -0
  124. package/dist/agents/n8n/N8nWorkflowExecutorAgent.js +347 -0
  125. package/dist/agents/n8n/N8nWorkflowExecutorAgent.js.map +1 -0
  126. package/dist/agents/n8n/index.d.ts +119 -0
  127. package/dist/agents/n8n/index.d.ts.map +1 -0
  128. package/dist/agents/n8n/index.js +298 -0
  129. package/dist/agents/n8n/index.js.map +1 -0
  130. package/dist/agents/n8n/types.d.ts +486 -0
  131. package/dist/agents/n8n/types.d.ts.map +1 -0
  132. package/dist/agents/n8n/types.js +8 -0
  133. package/dist/agents/n8n/types.js.map +1 -0
  134. package/dist/agents/utils/generators.d.ts +30 -0
  135. package/dist/agents/utils/generators.d.ts.map +1 -0
  136. package/dist/agents/utils/generators.js +44 -0
  137. package/dist/agents/utils/generators.js.map +1 -0
  138. package/dist/agents/utils/index.d.ts +10 -0
  139. package/dist/agents/utils/index.d.ts.map +1 -0
  140. package/dist/agents/utils/index.js +19 -0
  141. package/dist/agents/utils/index.js.map +1 -0
  142. package/dist/agents/utils/validation.d.ts +72 -0
  143. package/dist/agents/utils/validation.d.ts.map +1 -0
  144. package/dist/agents/utils/validation.js +75 -0
  145. package/dist/agents/utils/validation.js.map +1 -0
  146. package/dist/cli/init/agents.d.ts.map +1 -1
  147. package/dist/cli/init/agents.js +29 -0
  148. package/dist/cli/init/agents.js.map +1 -1
  149. package/dist/cli/init/skills.d.ts.map +1 -1
  150. package/dist/cli/init/skills.js +7 -1
  151. package/dist/cli/init/skills.js.map +1 -1
  152. package/dist/core/memory/HNSWVectorMemory.js +1 -1
  153. package/dist/core/memory/SwarmMemoryManager.d.ts +114 -90
  154. package/dist/core/memory/SwarmMemoryManager.d.ts.map +1 -1
  155. package/dist/core/memory/SwarmMemoryManager.js +277 -235
  156. package/dist/core/memory/SwarmMemoryManager.js.map +1 -1
  157. package/dist/learning/baselines/StandardTaskSuite.d.ts.map +1 -1
  158. package/dist/learning/baselines/StandardTaskSuite.js +38 -0
  159. package/dist/learning/baselines/StandardTaskSuite.js.map +1 -1
  160. package/dist/mcp/server-instructions.d.ts +1 -1
  161. package/dist/mcp/server-instructions.js +1 -1
  162. package/dist/types/memory-interfaces.d.ts +76 -68
  163. package/dist/types/memory-interfaces.d.ts.map +1 -1
  164. package/dist/types/memory-interfaces.js +3 -0
  165. package/dist/types/memory-interfaces.js.map +1 -1
  166. package/docs/reference/agents.md +91 -2
  167. package/docs/reference/skills.md +97 -2
  168. package/package.json +2 -2
@@ -0,0 +1,599 @@
1
+ ---
2
+ name: n8n-security-testing
3
+ description: "Credential exposure detection, OAuth flow validation, API key management testing, and data sanitization verification for n8n workflows. Use when validating n8n workflow security."
4
+ category: n8n-testing
5
+ priority: critical
6
+ tokenEstimate: 1100
7
+ agents: [n8n-integration-test]
8
+ implementation_status: production
9
+ optimization_version: 1.0
10
+ last_optimized: 2025-12-15
11
+ dependencies: []
12
+ quick_reference_card: true
13
+ tags: [n8n, security, credentials, oauth, api-keys, encryption, testing]
14
+ ---
15
+
16
+ # n8n Security Testing
17
+
18
+ <default_to_action>
19
+ When testing n8n security:
20
+ 1. SCAN for credential exposure in workflows
21
+ 2. VERIFY encryption of sensitive data
22
+ 3. TEST OAuth token handling
23
+ 4. CHECK for insecure data transmission
24
+ 5. VALIDATE input sanitization
25
+
26
+ **Quick Security Checklist:**
27
+ - No credentials in workflow JSON
28
+ - No credentials in execution logs
29
+ - OAuth tokens properly encrypted
30
+ - API keys not in version control
31
+ - Webhook authentication enabled
32
+ - Input data sanitized
33
+
34
+ **Critical Success Factors:**
35
+ - Scan all workflow exports
36
+ - Test credential rotation
37
+ - Verify encryption at rest
38
+ - Check audit logging
39
+ </default_to_action>
40
+
41
+ ## Quick Reference Card
42
+
43
+ ### Security Risk Areas
44
+
45
+ | Area | Risk Level | Testing Focus |
46
+ |------|------------|---------------|
47
+ | **Credential Storage** | Critical | Encryption, exposure |
48
+ | **Webhook Security** | High | Authentication, validation |
49
+ | **Expression Injection** | High | Input sanitization |
50
+ | **Data Leakage** | Medium | Logging, error messages |
51
+ | **OAuth Flows** | Medium | Token handling, refresh |
52
+
53
+ ### Credential Types
54
+
55
+ | Type | Exposure Risk | Rotation |
56
+ |------|---------------|----------|
57
+ | **API Keys** | High if exposed | Manual |
58
+ | **OAuth Tokens** | Medium (short-lived) | Automatic |
59
+ | **Passwords** | Critical | Manual |
60
+ | **Webhooks** | Medium | Generate new |
61
+
62
+ ---
63
+
64
+ ## Credential Security Testing
65
+
66
+ ### Scan for Exposed Credentials
67
+
68
+ ```typescript
69
+ // Scan workflow JSON for credential exposure
70
+ async function scanForExposedCredentials(workflowId: string): Promise<CredentialScanResult> {
71
+ const workflow = await getWorkflow(workflowId);
72
+ const workflowJson = JSON.stringify(workflow, null, 2);
73
+
74
+ const sensitivePatterns = [
75
+ // API Keys
76
+ { name: 'Generic API Key', pattern: /api[_-]?key["\s:=]+["']?([a-zA-Z0-9_-]{20,})["']?/gi },
77
+ { name: 'AWS Access Key', pattern: /AKIA[0-9A-Z]{16}/g },
78
+ { name: 'AWS Secret Key', pattern: /[a-zA-Z0-9/+=]{40}/g },
79
+ // Tokens
80
+ { name: 'Bearer Token', pattern: /bearer\s+[a-zA-Z0-9_-]{20,}/gi },
81
+ { name: 'JWT Token', pattern: /eyJ[a-zA-Z0-9_-]*\.eyJ[a-zA-Z0-9_-]*\.[a-zA-Z0-9_-]*/g },
82
+ { name: 'Slack Token', pattern: /xox[baprs]-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24}/g },
83
+ // Passwords
84
+ { name: 'Password Field', pattern: /"password":\s*"[^"]+"/gi },
85
+ { name: 'Secret Field', pattern: /"secret":\s*"[^"]+"/gi },
86
+ // OAuth
87
+ { name: 'Client Secret', pattern: /client[_-]?secret["\s:=]+["']?([a-zA-Z0-9_-]{20,})["']?/gi },
88
+ { name: 'Refresh Token', pattern: /refresh[_-]?token["\s:=]+["']?([a-zA-Z0-9_-]{20,})["']?/gi }
89
+ ];
90
+
91
+ const findings: CredentialFinding[] = [];
92
+
93
+ for (const pattern of sensitivePatterns) {
94
+ const matches = workflowJson.match(pattern.pattern);
95
+ if (matches) {
96
+ for (const match of matches) {
97
+ findings.push({
98
+ type: pattern.name,
99
+ location: findLocationInWorkflow(workflow, match),
100
+ severity: 'CRITICAL',
101
+ recommendation: `Remove ${pattern.name} from workflow. Use n8n credentials instead.`
102
+ });
103
+ }
104
+ }
105
+ }
106
+
107
+ return {
108
+ workflowId,
109
+ scanned: true,
110
+ findingsCount: findings.length,
111
+ findings,
112
+ secure: findings.length === 0
113
+ };
114
+ }
115
+ ```
116
+
117
+ ### Verify Credential Encryption
118
+
119
+ ```typescript
120
+ // Verify credentials are encrypted at rest
121
+ async function verifyCredentialEncryption(credentialId: string): Promise<EncryptionResult> {
122
+ // Get credential metadata (not the actual credential)
123
+ const credential = await getCredentialMetadata(credentialId);
124
+
125
+ // Check if credential data is encrypted
126
+ const encryptionChecks = {
127
+ // Check if stored data looks encrypted (not plain text)
128
+ isEncrypted: !isPlainText(credential.data),
129
+ // Check encryption algorithm
130
+ algorithm: credential.encryptionAlgorithm || 'unknown',
131
+ // Check key derivation
132
+ keyDerivation: credential.keyDerivation || 'unknown',
133
+ // Check if using instance encryption key
134
+ instanceEncryption: credential.useInstanceKey || false
135
+ };
136
+
137
+ return {
138
+ credentialId,
139
+ credentialName: credential.name,
140
+ credentialType: credential.type,
141
+ encryption: encryptionChecks,
142
+ secure: encryptionChecks.isEncrypted && encryptionChecks.algorithm !== 'unknown',
143
+ recommendations: generateEncryptionRecommendations(encryptionChecks)
144
+ };
145
+ }
146
+
147
+ // Check if data appears to be plain text
148
+ function isPlainText(data: string): boolean {
149
+ // Plain text credentials often have recognizable patterns
150
+ const plainTextPatterns = [
151
+ /^[a-zA-Z0-9_-]+$/, // Simple alphanumeric
152
+ /^sk-[a-zA-Z0-9]+$/, // API key format
153
+ /^Bearer\s/, // Bearer token
154
+ ];
155
+
156
+ return plainTextPatterns.some(p => p.test(data));
157
+ }
158
+ ```
159
+
160
+ ### Test Credential Rotation
161
+
162
+ ```typescript
163
+ // Test credential rotation process
164
+ async function testCredentialRotation(credentialId: string): Promise<RotationTestResult> {
165
+ const credential = await getCredentialMetadata(credentialId);
166
+
167
+ const rotationTests = {
168
+ // Check if credential has rotation metadata
169
+ hasRotationSchedule: !!credential.rotationSchedule,
170
+ lastRotated: credential.lastRotatedAt,
171
+ rotationDue: isRotationDue(credential),
172
+
173
+ // Test OAuth token refresh
174
+ oauthRefresh: credential.type.includes('oauth')
175
+ ? await testOAuthRefresh(credentialId)
176
+ : null,
177
+
178
+ // Check credential age
179
+ credentialAge: calculateAge(credential.createdAt),
180
+ isStale: calculateAge(credential.createdAt) > 90 // 90 days
181
+ };
182
+
183
+ return {
184
+ credentialId,
185
+ rotationTests,
186
+ recommendations: generateRotationRecommendations(rotationTests)
187
+ };
188
+ }
189
+
190
+ // Test OAuth token refresh
191
+ async function testOAuthRefresh(credentialId: string): Promise<OAuthRefreshResult> {
192
+ try {
193
+ // Trigger refresh
194
+ const refreshed = await refreshCredential(credentialId);
195
+
196
+ return {
197
+ success: true,
198
+ newExpiry: refreshed.expiresAt,
199
+ refreshedAt: new Date()
200
+ };
201
+ } catch (error) {
202
+ return {
203
+ success: false,
204
+ error: error.message,
205
+ recommendation: 'Re-authorize OAuth connection'
206
+ };
207
+ }
208
+ }
209
+ ```
210
+
211
+ ---
212
+
213
+ ## Webhook Security Testing
214
+
215
+ ### Authentication Testing
216
+
217
+ ```typescript
218
+ // Test webhook authentication enforcement
219
+ async function testWebhookAuthentication(webhookUrl: string): Promise<WebhookAuthResult> {
220
+ const authTests = [
221
+ // No authentication
222
+ {
223
+ name: 'No Auth',
224
+ headers: {},
225
+ expectedStatus: 401
226
+ },
227
+ // Invalid Basic Auth
228
+ {
229
+ name: 'Invalid Basic Auth',
230
+ headers: { 'Authorization': 'Basic aW52YWxpZDppbnZhbGlk' },
231
+ expectedStatus: 401
232
+ },
233
+ // Invalid Bearer Token
234
+ {
235
+ name: 'Invalid Bearer',
236
+ headers: { 'Authorization': 'Bearer invalid-token-12345' },
237
+ expectedStatus: 401
238
+ },
239
+ // Invalid Header Auth
240
+ {
241
+ name: 'Invalid Header Auth',
242
+ headers: { 'X-API-Key': 'invalid-key' },
243
+ expectedStatus: 401
244
+ }
245
+ ];
246
+
247
+ const results: AuthTestResult[] = [];
248
+
249
+ for (const test of authTests) {
250
+ const response = await fetch(webhookUrl, {
251
+ method: 'POST',
252
+ headers: {
253
+ 'Content-Type': 'application/json',
254
+ ...test.headers
255
+ },
256
+ body: '{}'
257
+ });
258
+
259
+ results.push({
260
+ test: test.name,
261
+ status: response.status,
262
+ passed: response.status === test.expectedStatus,
263
+ actualStatus: response.status,
264
+ expectedStatus: test.expectedStatus
265
+ });
266
+ }
267
+
268
+ // Check if webhook has ANY auth
269
+ const noAuthResponse = results.find(r => r.test === 'No Auth');
270
+ const webhookHasAuth = noAuthResponse?.status === 401;
271
+
272
+ return {
273
+ webhookUrl,
274
+ hasAuthentication: webhookHasAuth,
275
+ testResults: results,
276
+ allTestsPassed: results.every(r => r.passed),
277
+ recommendation: !webhookHasAuth
278
+ ? 'CRITICAL: Enable authentication on webhook'
279
+ : null
280
+ };
281
+ }
282
+ ```
283
+
284
+ ### Input Validation Testing
285
+
286
+ ```typescript
287
+ // Test webhook input validation
288
+ async function testWebhookInputValidation(webhookUrl: string): Promise<InputValidationResult> {
289
+ const maliciousPayloads = [
290
+ // XSS attempts
291
+ {
292
+ name: 'XSS Script Tag',
293
+ payload: { text: '<script>alert("xss")</script>' },
294
+ check: 'sanitized'
295
+ },
296
+ {
297
+ name: 'XSS Event Handler',
298
+ payload: { text: '<img onerror="alert(1)" src="x">' },
299
+ check: 'sanitized'
300
+ },
301
+ // SQL Injection
302
+ {
303
+ name: 'SQL Injection',
304
+ payload: { id: "1; DROP TABLE users; --" },
305
+ check: 'escaped'
306
+ },
307
+ // Command Injection
308
+ {
309
+ name: 'Command Injection',
310
+ payload: { filename: '; rm -rf /' },
311
+ check: 'rejected'
312
+ },
313
+ // Path Traversal
314
+ {
315
+ name: 'Path Traversal',
316
+ payload: { path: '../../../etc/passwd' },
317
+ check: 'rejected'
318
+ },
319
+ // JSON Injection
320
+ {
321
+ name: 'JSON Injection',
322
+ payload: { data: '{"admin": true}' },
323
+ check: 'escaped'
324
+ },
325
+ // Oversized payload
326
+ {
327
+ name: 'Oversized Payload',
328
+ payload: { data: 'x'.repeat(10000000) }, // 10MB
329
+ check: 'rejected'
330
+ }
331
+ ];
332
+
333
+ const results: ValidationTestResult[] = [];
334
+
335
+ for (const test of maliciousPayloads) {
336
+ try {
337
+ const response = await fetch(webhookUrl, {
338
+ method: 'POST',
339
+ headers: { 'Content-Type': 'application/json' },
340
+ body: JSON.stringify(test.payload)
341
+ });
342
+
343
+ const responseBody = await response.text();
344
+
345
+ results.push({
346
+ test: test.name,
347
+ status: response.status,
348
+ handled: response.status !== 500, // Not a server error
349
+ sanitized: !responseBody.includes(test.payload.text || test.payload.data),
350
+ recommendation: response.status === 500
351
+ ? `Input not handled safely: ${test.name}`
352
+ : null
353
+ });
354
+ } catch (error) {
355
+ results.push({
356
+ test: test.name,
357
+ handled: false,
358
+ error: error.message
359
+ });
360
+ }
361
+ }
362
+
363
+ return {
364
+ webhookUrl,
365
+ testsRun: maliciousPayloads.length,
366
+ passed: results.filter(r => r.handled).length,
367
+ failed: results.filter(r => !r.handled).length,
368
+ results,
369
+ secure: results.every(r => r.handled)
370
+ };
371
+ }
372
+ ```
373
+
374
+ ---
375
+
376
+ ## Expression Security Testing
377
+
378
+ ### Detect Dangerous Expressions
379
+
380
+ ```typescript
381
+ // Scan expressions for security vulnerabilities
382
+ async function scanExpressionsForSecurity(workflowId: string): Promise<ExpressionSecurityResult> {
383
+ const workflow = await getWorkflow(workflowId);
384
+ const expressions = extractExpressions(workflow);
385
+
386
+ const dangerousPatterns = [
387
+ // Code execution
388
+ { name: 'eval()', pattern: /eval\s*\(/g, severity: 'CRITICAL' },
389
+ { name: 'Function()', pattern: /new\s+Function\s*\(/g, severity: 'CRITICAL' },
390
+ { name: 'setTimeout string', pattern: /setTimeout\s*\(\s*["'`]/g, severity: 'HIGH' },
391
+ { name: 'setInterval string', pattern: /setInterval\s*\(\s*["'`]/g, severity: 'HIGH' },
392
+
393
+ // File system access
394
+ { name: 'require()', pattern: /require\s*\(/g, severity: 'HIGH' },
395
+ { name: 'import()', pattern: /import\s*\(/g, severity: 'HIGH' },
396
+ { name: 'fs access', pattern: /\bfs\./g, severity: 'HIGH' },
397
+
398
+ // Process/child execution
399
+ { name: 'child_process', pattern: /child_process/g, severity: 'CRITICAL' },
400
+ { name: 'process.', pattern: /process\./g, severity: 'MEDIUM' },
401
+ { name: 'exec()', pattern: /exec\s*\(/g, severity: 'CRITICAL' },
402
+ { name: 'spawn()', pattern: /spawn\s*\(/g, severity: 'CRITICAL' },
403
+
404
+ // Network access
405
+ { name: 'fetch()', pattern: /fetch\s*\(/g, severity: 'MEDIUM' },
406
+ { name: 'XMLHttpRequest', pattern: /XMLHttpRequest/g, severity: 'MEDIUM' },
407
+
408
+ // Prototype pollution
409
+ { name: '__proto__', pattern: /__proto__/g, severity: 'HIGH' },
410
+ { name: 'constructor.prototype', pattern: /constructor\.prototype/g, severity: 'HIGH' }
411
+ ];
412
+
413
+ const findings: SecurityFinding[] = [];
414
+
415
+ for (const expr of expressions) {
416
+ for (const pattern of dangerousPatterns) {
417
+ if (pattern.pattern.test(expr.expression)) {
418
+ findings.push({
419
+ node: expr.nodeName,
420
+ parameter: expr.parameter,
421
+ expression: expr.expression,
422
+ pattern: pattern.name,
423
+ severity: pattern.severity,
424
+ recommendation: `Remove ${pattern.name} from expression. Use safer alternatives.`
425
+ });
426
+ }
427
+ }
428
+ }
429
+
430
+ return {
431
+ workflowId,
432
+ expressionsScanned: expressions.length,
433
+ findings,
434
+ secure: findings.length === 0,
435
+ criticalIssues: findings.filter(f => f.severity === 'CRITICAL').length,
436
+ highIssues: findings.filter(f => f.severity === 'HIGH').length
437
+ };
438
+ }
439
+ ```
440
+
441
+ ---
442
+
443
+ ## Data Leakage Testing
444
+
445
+ ### Scan Execution Logs
446
+
447
+ ```typescript
448
+ // Scan execution logs for credential leakage
449
+ async function scanExecutionLogs(workflowId: string, executionCount: number = 10): Promise<LogScanResult> {
450
+ const executions = await getRecentExecutions(workflowId, executionCount);
451
+ const findings: LogFinding[] = [];
452
+
453
+ const sensitivePatterns = [
454
+ { name: 'Password', pattern: /password["\s:=]+["']?[^"'\s]+["']?/gi },
455
+ { name: 'API Key', pattern: /api[_-]?key["\s:=]+["']?[^"'\s]{20,}["']?/gi },
456
+ { name: 'Token', pattern: /token["\s:=]+["']?[a-zA-Z0-9_-]{20,}["']?/gi },
457
+ { name: 'Secret', pattern: /secret["\s:=]+["']?[^"'\s]+["']?/gi },
458
+ { name: 'Authorization Header', pattern: /authorization["\s:]+["']?(bearer|basic)\s+[^"'\s]+["']?/gi }
459
+ ];
460
+
461
+ for (const execution of executions) {
462
+ const logString = JSON.stringify(execution.data, null, 2);
463
+
464
+ for (const pattern of sensitivePatterns) {
465
+ const matches = logString.match(pattern.pattern);
466
+ if (matches) {
467
+ findings.push({
468
+ executionId: execution.id,
469
+ type: pattern.name,
470
+ matchCount: matches.length,
471
+ severity: 'HIGH',
472
+ recommendation: `Mask ${pattern.name} in logs`
473
+ });
474
+ }
475
+ }
476
+ }
477
+
478
+ return {
479
+ workflowId,
480
+ executionsScanned: executions.length,
481
+ findings,
482
+ secure: findings.length === 0,
483
+ recommendation: findings.length > 0
484
+ ? 'Enable credential masking in n8n settings'
485
+ : null
486
+ };
487
+ }
488
+ ```
489
+
490
+ ### Check Error Message Exposure
491
+
492
+ ```typescript
493
+ // Check if error messages expose sensitive information
494
+ async function checkErrorMessageSecurity(workflowId: string): Promise<ErrorMessageResult> {
495
+ // Trigger intentional errors
496
+ const errorScenarios = [
497
+ { name: 'Invalid credentials', inject: { credentials: null } },
498
+ { name: 'Invalid endpoint', inject: { url: 'https://invalid' } },
499
+ { name: 'Database error', inject: { query: 'INVALID SQL' } }
500
+ ];
501
+
502
+ const findings: ErrorFinding[] = [];
503
+
504
+ for (const scenario of errorScenarios) {
505
+ try {
506
+ await executeWithError(workflowId, scenario.inject);
507
+ } catch (error) {
508
+ const errorMessage = error.message;
509
+
510
+ // Check for sensitive data in error
511
+ const sensitiveData = [
512
+ { name: 'Connection string', pattern: /mongodb:\/\/[^@]+@/i },
513
+ { name: 'Password in URL', pattern: /:\/\/[^:]+:[^@]+@/i },
514
+ { name: 'Full file path', pattern: /\/(?:home|Users|var)\/[^\s]+/i },
515
+ { name: 'Stack trace', pattern: /at\s+\w+\s+\([^)]+\)/i },
516
+ { name: 'Internal IP', pattern: /\b(?:10|172\.(?:1[6-9]|2[0-9]|3[01])|192\.168)\.\d+\.\d+\b/i }
517
+ ];
518
+
519
+ for (const check of sensitiveData) {
520
+ if (check.pattern.test(errorMessage)) {
521
+ findings.push({
522
+ scenario: scenario.name,
523
+ exposedData: check.name,
524
+ severity: 'MEDIUM',
525
+ recommendation: `Sanitize ${check.name} from error messages`
526
+ });
527
+ }
528
+ }
529
+ }
530
+ }
531
+
532
+ return {
533
+ workflowId,
534
+ scenariosTested: errorScenarios.length,
535
+ findings,
536
+ secure: findings.length === 0
537
+ };
538
+ }
539
+ ```
540
+
541
+ ---
542
+
543
+ ## Security Report Template
544
+
545
+ ```markdown
546
+ # n8n Security Audit Report
547
+
548
+ ## Summary
549
+ | Category | Status | Findings |
550
+ |----------|--------|----------|
551
+ | Credential Security | PASS/FAIL | X issues |
552
+ | Webhook Security | PASS/FAIL | X issues |
553
+ | Expression Security | PASS/FAIL | X issues |
554
+ | Data Leakage | PASS/FAIL | X issues |
555
+
556
+ ## Critical Findings
557
+
558
+ ### CRIT-001: API Key Exposed in Workflow
559
+ - **Location:** HTTP Request node, URL parameter
560
+ - **Impact:** Credential theft, unauthorized access
561
+ - **Fix:** Move to n8n credentials store
562
+
563
+ ### CRIT-002: eval() in Expression
564
+ - **Location:** Set node, custom field
565
+ - **Impact:** Remote code execution
566
+ - **Fix:** Remove eval, use explicit logic
567
+
568
+ ## Recommendations
569
+
570
+ 1. **Enable webhook authentication** - All public webhooks
571
+ 2. **Rotate exposed credentials** - Immediately
572
+ 3. **Enable log masking** - For all credentials
573
+ 4. **Regular security scans** - Weekly automated scans
574
+
575
+ ## Compliance Status
576
+ - OWASP Top 10: X/10 addressed
577
+ - SOC 2: Partially compliant
578
+ - GDPR: Review data handling
579
+ ```
580
+
581
+ ---
582
+
583
+ ## Related Skills
584
+ - [n8n-workflow-testing-fundamentals](../n8n-workflow-testing-fundamentals/)
585
+ - [n8n-integration-testing-patterns](../n8n-integration-testing-patterns/)
586
+ - [compliance-testing](../compliance-testing/)
587
+
588
+ ---
589
+
590
+ ## Remember
591
+
592
+ **n8n handles sensitive credentials** for 400+ integrations. Security testing requires:
593
+ - Credential exposure scanning
594
+ - Encryption verification
595
+ - Webhook authentication testing
596
+ - Expression security analysis
597
+ - Data leakage detection
598
+
599
+ **Critical practices:** Never expose credentials in workflow JSON. Enable webhook authentication. Mask sensitive data in logs. Rotate credentials regularly. Scan expressions for dangerous functions.