agentic-qe 2.5.6 → 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 (134) 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 +41 -0
  22. package/README.md +7 -4
  23. package/dist/agents/n8n/N8nAPIClient.d.ts +121 -0
  24. package/dist/agents/n8n/N8nAPIClient.d.ts.map +1 -0
  25. package/dist/agents/n8n/N8nAPIClient.js +367 -0
  26. package/dist/agents/n8n/N8nAPIClient.js.map +1 -0
  27. package/dist/agents/n8n/N8nAuditPersistence.d.ts +120 -0
  28. package/dist/agents/n8n/N8nAuditPersistence.d.ts.map +1 -0
  29. package/dist/agents/n8n/N8nAuditPersistence.js +473 -0
  30. package/dist/agents/n8n/N8nAuditPersistence.js.map +1 -0
  31. package/dist/agents/n8n/N8nBDDScenarioTesterAgent.d.ts +159 -0
  32. package/dist/agents/n8n/N8nBDDScenarioTesterAgent.d.ts.map +1 -0
  33. package/dist/agents/n8n/N8nBDDScenarioTesterAgent.js +697 -0
  34. package/dist/agents/n8n/N8nBDDScenarioTesterAgent.js.map +1 -0
  35. package/dist/agents/n8n/N8nBaseAgent.d.ts +126 -0
  36. package/dist/agents/n8n/N8nBaseAgent.d.ts.map +1 -0
  37. package/dist/agents/n8n/N8nBaseAgent.js +446 -0
  38. package/dist/agents/n8n/N8nBaseAgent.js.map +1 -0
  39. package/dist/agents/n8n/N8nCIOrchestratorAgent.d.ts +164 -0
  40. package/dist/agents/n8n/N8nCIOrchestratorAgent.d.ts.map +1 -0
  41. package/dist/agents/n8n/N8nCIOrchestratorAgent.js +610 -0
  42. package/dist/agents/n8n/N8nCIOrchestratorAgent.js.map +1 -0
  43. package/dist/agents/n8n/N8nChaosTesterAgent.d.ts +205 -0
  44. package/dist/agents/n8n/N8nChaosTesterAgent.d.ts.map +1 -0
  45. package/dist/agents/n8n/N8nChaosTesterAgent.js +729 -0
  46. package/dist/agents/n8n/N8nChaosTesterAgent.js.map +1 -0
  47. package/dist/agents/n8n/N8nComplianceValidatorAgent.d.ts +228 -0
  48. package/dist/agents/n8n/N8nComplianceValidatorAgent.d.ts.map +1 -0
  49. package/dist/agents/n8n/N8nComplianceValidatorAgent.js +986 -0
  50. package/dist/agents/n8n/N8nComplianceValidatorAgent.js.map +1 -0
  51. package/dist/agents/n8n/N8nContractTesterAgent.d.ts +213 -0
  52. package/dist/agents/n8n/N8nContractTesterAgent.d.ts.map +1 -0
  53. package/dist/agents/n8n/N8nContractTesterAgent.js +989 -0
  54. package/dist/agents/n8n/N8nContractTesterAgent.js.map +1 -0
  55. package/dist/agents/n8n/N8nExpressionValidatorAgent.d.ts +99 -0
  56. package/dist/agents/n8n/N8nExpressionValidatorAgent.d.ts.map +1 -0
  57. package/dist/agents/n8n/N8nExpressionValidatorAgent.js +632 -0
  58. package/dist/agents/n8n/N8nExpressionValidatorAgent.js.map +1 -0
  59. package/dist/agents/n8n/N8nFailureModeTesterAgent.d.ts +238 -0
  60. package/dist/agents/n8n/N8nFailureModeTesterAgent.d.ts.map +1 -0
  61. package/dist/agents/n8n/N8nFailureModeTesterAgent.js +956 -0
  62. package/dist/agents/n8n/N8nFailureModeTesterAgent.js.map +1 -0
  63. package/dist/agents/n8n/N8nIdempotencyTesterAgent.d.ts +242 -0
  64. package/dist/agents/n8n/N8nIdempotencyTesterAgent.d.ts.map +1 -0
  65. package/dist/agents/n8n/N8nIdempotencyTesterAgent.js +992 -0
  66. package/dist/agents/n8n/N8nIdempotencyTesterAgent.js.map +1 -0
  67. package/dist/agents/n8n/N8nIntegrationTestAgent.d.ts +104 -0
  68. package/dist/agents/n8n/N8nIntegrationTestAgent.d.ts.map +1 -0
  69. package/dist/agents/n8n/N8nIntegrationTestAgent.js +653 -0
  70. package/dist/agents/n8n/N8nIntegrationTestAgent.js.map +1 -0
  71. package/dist/agents/n8n/N8nMonitoringValidatorAgent.d.ts +210 -0
  72. package/dist/agents/n8n/N8nMonitoringValidatorAgent.d.ts.map +1 -0
  73. package/dist/agents/n8n/N8nMonitoringValidatorAgent.js +669 -0
  74. package/dist/agents/n8n/N8nMonitoringValidatorAgent.js.map +1 -0
  75. package/dist/agents/n8n/N8nNodeValidatorAgent.d.ts +142 -0
  76. package/dist/agents/n8n/N8nNodeValidatorAgent.d.ts.map +1 -0
  77. package/dist/agents/n8n/N8nNodeValidatorAgent.js +1090 -0
  78. package/dist/agents/n8n/N8nNodeValidatorAgent.js.map +1 -0
  79. package/dist/agents/n8n/N8nPerformanceTesterAgent.d.ts +198 -0
  80. package/dist/agents/n8n/N8nPerformanceTesterAgent.d.ts.map +1 -0
  81. package/dist/agents/n8n/N8nPerformanceTesterAgent.js +653 -0
  82. package/dist/agents/n8n/N8nPerformanceTesterAgent.js.map +1 -0
  83. package/dist/agents/n8n/N8nReplayabilityTesterAgent.d.ts +245 -0
  84. package/dist/agents/n8n/N8nReplayabilityTesterAgent.d.ts.map +1 -0
  85. package/dist/agents/n8n/N8nReplayabilityTesterAgent.js +952 -0
  86. package/dist/agents/n8n/N8nReplayabilityTesterAgent.js.map +1 -0
  87. package/dist/agents/n8n/N8nSecretsHygieneAuditorAgent.d.ts +325 -0
  88. package/dist/agents/n8n/N8nSecretsHygieneAuditorAgent.d.ts.map +1 -0
  89. package/dist/agents/n8n/N8nSecretsHygieneAuditorAgent.js +1187 -0
  90. package/dist/agents/n8n/N8nSecretsHygieneAuditorAgent.js.map +1 -0
  91. package/dist/agents/n8n/N8nSecurityAuditorAgent.d.ts +91 -0
  92. package/dist/agents/n8n/N8nSecurityAuditorAgent.d.ts.map +1 -0
  93. package/dist/agents/n8n/N8nSecurityAuditorAgent.js +825 -0
  94. package/dist/agents/n8n/N8nSecurityAuditorAgent.js.map +1 -0
  95. package/dist/agents/n8n/N8nTestHarness.d.ts +131 -0
  96. package/dist/agents/n8n/N8nTestHarness.d.ts.map +1 -0
  97. package/dist/agents/n8n/N8nTestHarness.js +456 -0
  98. package/dist/agents/n8n/N8nTestHarness.js.map +1 -0
  99. package/dist/agents/n8n/N8nTriggerTestAgent.d.ts +119 -0
  100. package/dist/agents/n8n/N8nTriggerTestAgent.d.ts.map +1 -0
  101. package/dist/agents/n8n/N8nTriggerTestAgent.js +652 -0
  102. package/dist/agents/n8n/N8nTriggerTestAgent.js.map +1 -0
  103. package/dist/agents/n8n/N8nUnitTesterAgent.d.ts +130 -0
  104. package/dist/agents/n8n/N8nUnitTesterAgent.d.ts.map +1 -0
  105. package/dist/agents/n8n/N8nUnitTesterAgent.js +522 -0
  106. package/dist/agents/n8n/N8nUnitTesterAgent.js.map +1 -0
  107. package/dist/agents/n8n/N8nVersionComparatorAgent.d.ts +201 -0
  108. package/dist/agents/n8n/N8nVersionComparatorAgent.d.ts.map +1 -0
  109. package/dist/agents/n8n/N8nVersionComparatorAgent.js +645 -0
  110. package/dist/agents/n8n/N8nVersionComparatorAgent.js.map +1 -0
  111. package/dist/agents/n8n/N8nWorkflowExecutorAgent.d.ts +120 -0
  112. package/dist/agents/n8n/N8nWorkflowExecutorAgent.d.ts.map +1 -0
  113. package/dist/agents/n8n/N8nWorkflowExecutorAgent.js +347 -0
  114. package/dist/agents/n8n/N8nWorkflowExecutorAgent.js.map +1 -0
  115. package/dist/agents/n8n/index.d.ts +119 -0
  116. package/dist/agents/n8n/index.d.ts.map +1 -0
  117. package/dist/agents/n8n/index.js +298 -0
  118. package/dist/agents/n8n/index.js.map +1 -0
  119. package/dist/agents/n8n/types.d.ts +486 -0
  120. package/dist/agents/n8n/types.d.ts.map +1 -0
  121. package/dist/agents/n8n/types.js +8 -0
  122. package/dist/agents/n8n/types.js.map +1 -0
  123. package/dist/cli/init/agents.d.ts.map +1 -1
  124. package/dist/cli/init/agents.js +29 -0
  125. package/dist/cli/init/agents.js.map +1 -1
  126. package/dist/cli/init/skills.d.ts.map +1 -1
  127. package/dist/cli/init/skills.js +7 -1
  128. package/dist/cli/init/skills.js.map +1 -1
  129. package/dist/core/memory/HNSWVectorMemory.js +1 -1
  130. package/dist/mcp/server-instructions.d.ts +1 -1
  131. package/dist/mcp/server-instructions.js +1 -1
  132. package/docs/reference/agents.md +91 -2
  133. package/docs/reference/skills.md +97 -2
  134. package/package.json +2 -2
@@ -0,0 +1,825 @@
1
+ "use strict";
2
+ /**
3
+ * N8nSecurityAuditorAgent
4
+ *
5
+ * Security vulnerability scanning for n8n workflows:
6
+ * - Credential exposure detection
7
+ * - Secret scanning in expressions
8
+ * - SQL/NoSQL injection risk analysis
9
+ * - Command injection detection
10
+ * - SSRF detection
11
+ * - OWASP compliance checking
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.N8nSecurityAuditorAgent = void 0;
15
+ const N8nBaseAgent_1 = require("./N8nBaseAgent");
16
+ // Secret patterns to detect - Extended: 40+ patterns (from ~11)
17
+ const SECRET_PATTERNS = [
18
+ // ============================================================================
19
+ // Generic Secrets
20
+ // ============================================================================
21
+ { pattern: /api[_-]?key["\s:=]+["']?[\w-]{20,}/i, name: 'API Key', severity: 'critical', category: 'generic' },
22
+ { pattern: /bearer\s+[\w-]{20,}/i, name: 'Bearer Token', severity: 'critical', category: 'generic' },
23
+ { pattern: /password["\s:=]+["']?[^"'\s]{8,}/i, name: 'Password', severity: 'critical', category: 'generic' },
24
+ { pattern: /secret["\s:=]+["']?[\w-]{20,}/i, name: 'Secret', severity: 'critical', category: 'generic' },
25
+ { pattern: /token["\s:=]+["']?[\w-]{20,}/i, name: 'Token', severity: 'critical', category: 'generic' },
26
+ { pattern: /-----BEGIN.*PRIVATE KEY-----/, name: 'Private Key', severity: 'critical', category: 'crypto' },
27
+ { pattern: /-----BEGIN.*RSA PRIVATE KEY-----/, name: 'RSA Private Key', severity: 'critical', category: 'crypto' },
28
+ { pattern: /-----BEGIN.*EC PRIVATE KEY-----/, name: 'EC Private Key', severity: 'critical', category: 'crypto' },
29
+ { pattern: /-----BEGIN.*OPENSSH PRIVATE KEY-----/, name: 'OpenSSH Private Key', severity: 'critical', category: 'crypto' },
30
+ { pattern: /-----BEGIN.*PGP PRIVATE KEY BLOCK-----/, name: 'PGP Private Key', severity: 'critical', category: 'crypto' },
31
+ // ============================================================================
32
+ // Cloud Providers
33
+ // ============================================================================
34
+ // AWS
35
+ { pattern: /AKIA[0-9A-Z]{16}/i, name: 'AWS Access Key ID', severity: 'critical', category: 'aws' },
36
+ { pattern: /aws[_-]?secret[_-]?access[_-]?key/i, name: 'AWS Secret Key Reference', severity: 'critical', category: 'aws' },
37
+ { pattern: /[a-zA-Z0-9\/+]{40}/, name: 'AWS Secret Access Key (potential)', severity: 'high', category: 'aws' },
38
+ // GCP
39
+ { pattern: /AIza[0-9A-Za-z-_]{35}/, name: 'Google API Key', severity: 'critical', category: 'gcp' },
40
+ { pattern: /[0-9]+-[0-9A-Za-z_]{32}\.apps\.googleusercontent\.com/, name: 'Google OAuth Client ID', severity: 'high', category: 'gcp' },
41
+ { pattern: /"type"\s*:\s*"service_account"/, name: 'GCP Service Account JSON', severity: 'critical', category: 'gcp' },
42
+ // Azure
43
+ { pattern: /[a-zA-Z0-9+\/]{43}=/, name: 'Azure Storage Key (potential)', severity: 'high', category: 'azure' },
44
+ { pattern: /DefaultEndpointsProtocol=https;AccountName=/i, name: 'Azure Connection String', severity: 'critical', category: 'azure' },
45
+ // ============================================================================
46
+ // AI/LLM Providers
47
+ // ============================================================================
48
+ { pattern: /sk-[a-zA-Z0-9]{32,}/, name: 'OpenAI API Key', severity: 'critical', category: 'ai' },
49
+ { pattern: /sk-ant-api[0-9a-zA-Z-]{90,}/, name: 'Anthropic API Key', severity: 'critical', category: 'ai' },
50
+ { pattern: /sk-or-[a-zA-Z0-9-]{40,}/, name: 'OpenRouter API Key', severity: 'critical', category: 'ai' },
51
+ { pattern: /r8_[a-zA-Z0-9]{37}/, name: 'Replicate API Key', severity: 'critical', category: 'ai' },
52
+ { pattern: /hf_[a-zA-Z0-9]{34}/, name: 'Hugging Face Token', severity: 'critical', category: 'ai' },
53
+ // ============================================================================
54
+ // Version Control & CI/CD
55
+ // ============================================================================
56
+ { pattern: /ghp_[a-zA-Z0-9]{36}/, name: 'GitHub Personal Access Token', severity: 'critical', category: 'vcs' },
57
+ { pattern: /gho_[a-zA-Z0-9]{36}/, name: 'GitHub OAuth Token', severity: 'critical', category: 'vcs' },
58
+ { pattern: /ghu_[a-zA-Z0-9]{36}/, name: 'GitHub User Token', severity: 'critical', category: 'vcs' },
59
+ { pattern: /ghs_[a-zA-Z0-9]{36}/, name: 'GitHub Server Token', severity: 'critical', category: 'vcs' },
60
+ { pattern: /github_pat_[a-zA-Z0-9_]{22,}/, name: 'GitHub Fine-grained PAT', severity: 'critical', category: 'vcs' },
61
+ { pattern: /glpat-[a-zA-Z0-9_-]{20}/, name: 'GitLab Personal Access Token', severity: 'critical', category: 'vcs' },
62
+ { pattern: /gloas-[a-zA-Z0-9_-]{20}/, name: 'GitLab OAuth App Secret', severity: 'critical', category: 'vcs' },
63
+ { pattern: /ATBB[a-zA-Z0-9]{24}/, name: 'Bitbucket App Token', severity: 'critical', category: 'vcs' },
64
+ // ============================================================================
65
+ // Communication Platforms
66
+ // ============================================================================
67
+ { pattern: /xox[baprs]-[\w-]+/, name: 'Slack Token', severity: 'critical', category: 'comms' },
68
+ { pattern: /hooks\.slack\.com\/services\/T[a-zA-Z0-9_]+\/B[a-zA-Z0-9_]+\/[a-zA-Z0-9_]+/, name: 'Slack Webhook URL', severity: 'high', category: 'comms' },
69
+ { pattern: /[MN][A-Za-z\d]{23,27}\.[A-Za-z\d_-]{6}\.[A-Za-z\d_-]{27,}/, name: 'Discord Bot Token', severity: 'critical', category: 'comms' },
70
+ { pattern: /discord\.com\/api\/webhooks\/[0-9]+\/[a-zA-Z0-9_-]+/, name: 'Discord Webhook URL', severity: 'high', category: 'comms' },
71
+ { pattern: /[0-9]+:AA[a-zA-Z0-9_-]{33}/, name: 'Telegram Bot Token', severity: 'critical', category: 'comms' },
72
+ { pattern: /EAA[a-zA-Z0-9]+/, name: 'Facebook Access Token', severity: 'critical', category: 'comms' },
73
+ // ============================================================================
74
+ // Payment & Financial
75
+ // ============================================================================
76
+ { pattern: /sk_live_[a-zA-Z0-9]{24,}/, name: 'Stripe Live Secret Key', severity: 'critical', category: 'payments' },
77
+ { pattern: /sk_test_[a-zA-Z0-9]{24,}/, name: 'Stripe Test Secret Key', severity: 'high', category: 'payments' },
78
+ { pattern: /pk_live_[a-zA-Z0-9]{24,}/, name: 'Stripe Live Publishable Key', severity: 'high', category: 'payments' },
79
+ { pattern: /rk_live_[a-zA-Z0-9]{24,}/, name: 'Stripe Live Restricted Key', severity: 'critical', category: 'payments' },
80
+ { pattern: /sq0atp-[a-zA-Z0-9_-]{22}/, name: 'Square Access Token', severity: 'critical', category: 'payments' },
81
+ { pattern: /sq0csp-[a-zA-Z0-9_-]{43}/, name: 'Square OAuth Secret', severity: 'critical', category: 'payments' },
82
+ { pattern: /access_token\$production\$[a-z0-9]{16}\$[a-f0-9]{32}/, name: 'PayPal Access Token', severity: 'critical', category: 'payments' },
83
+ // ============================================================================
84
+ // Database Connection Strings
85
+ // ============================================================================
86
+ { pattern: /mongodb(\+srv)?:\/\/[^:]+:[^@]+@/, name: 'MongoDB Connection String', severity: 'critical', category: 'database' },
87
+ { pattern: /postgres:\/\/[^:]+:[^@]+@/, name: 'PostgreSQL Connection String', severity: 'critical', category: 'database' },
88
+ { pattern: /mysql:\/\/[^:]+:[^@]+@/, name: 'MySQL Connection String', severity: 'critical', category: 'database' },
89
+ { pattern: /redis:\/\/:[^@]+@/, name: 'Redis Connection String', severity: 'critical', category: 'database' },
90
+ // ============================================================================
91
+ // Email & SMS Providers
92
+ // ============================================================================
93
+ { pattern: /SG\.[a-zA-Z0-9_-]{22}\.[a-zA-Z0-9_-]{43}/, name: 'SendGrid API Key', severity: 'critical', category: 'email' },
94
+ { pattern: /key-[a-f0-9]{32}/, name: 'Mailgun API Key', severity: 'critical', category: 'email' },
95
+ { pattern: /AC[a-f0-9]{32}/, name: 'Twilio Account SID', severity: 'high', category: 'sms' },
96
+ { pattern: /SK[a-f0-9]{32}/, name: 'Twilio API Key', severity: 'critical', category: 'sms' },
97
+ ];
98
+ // Injection patterns to detect - Extended: 20+ patterns (from ~4)
99
+ const INJECTION_PATTERNS = [
100
+ // ============================================================================
101
+ // SQL Injection Patterns
102
+ // ============================================================================
103
+ {
104
+ pattern: /'?\s*\+\s*\$json\./,
105
+ name: 'SQL String Concatenation',
106
+ type: 'sql_injection',
107
+ severity: 'high',
108
+ cwe: 'CWE-89',
109
+ },
110
+ {
111
+ pattern: /'\{\{\s*\$json\.[^}]+\}\}'/,
112
+ name: 'SQL Injection via Expression',
113
+ type: 'sql_injection',
114
+ severity: 'high',
115
+ cwe: 'CWE-89',
116
+ },
117
+ {
118
+ pattern: /SELECT\s+.*\s+FROM\s+.*\s+WHERE\s+.*\{\{/i,
119
+ name: 'Dynamic SQL WHERE clause',
120
+ type: 'sql_injection',
121
+ severity: 'high',
122
+ cwe: 'CWE-89',
123
+ },
124
+ {
125
+ pattern: /INSERT\s+INTO\s+.*VALUES\s*\(.*\{\{/i,
126
+ name: 'Dynamic SQL INSERT values',
127
+ type: 'sql_injection',
128
+ severity: 'high',
129
+ cwe: 'CWE-89',
130
+ },
131
+ {
132
+ pattern: /UPDATE\s+.*SET\s+.*=\s*.*\{\{/i,
133
+ name: 'Dynamic SQL UPDATE values',
134
+ type: 'sql_injection',
135
+ severity: 'high',
136
+ cwe: 'CWE-89',
137
+ },
138
+ {
139
+ pattern: /ORDER\s+BY\s+\{\{/i,
140
+ name: 'Dynamic SQL ORDER BY',
141
+ type: 'sql_injection',
142
+ severity: 'medium',
143
+ cwe: 'CWE-89',
144
+ },
145
+ // ============================================================================
146
+ // NoSQL Injection Patterns
147
+ // ============================================================================
148
+ {
149
+ pattern: /\$where\s*:\s*.*\{\{/,
150
+ name: 'MongoDB $where injection',
151
+ type: 'sql_injection',
152
+ severity: 'critical',
153
+ cwe: 'CWE-943',
154
+ },
155
+ {
156
+ pattern: /\$regex\s*:\s*.*\{\{/,
157
+ name: 'MongoDB $regex injection',
158
+ type: 'sql_injection',
159
+ severity: 'high',
160
+ cwe: 'CWE-943',
161
+ },
162
+ {
163
+ pattern: /\{\s*"\$[a-z]+"\s*:\s*\{\{/i,
164
+ name: 'MongoDB operator injection',
165
+ type: 'sql_injection',
166
+ severity: 'high',
167
+ cwe: 'CWE-943',
168
+ },
169
+ // ============================================================================
170
+ // Command Injection Patterns
171
+ // ============================================================================
172
+ {
173
+ pattern: /\$\{?\$json\.[^}]*\}?\s*[;&|`]/,
174
+ name: 'Shell Metacharacter Injection',
175
+ type: 'command_injection',
176
+ severity: 'critical',
177
+ cwe: 'CWE-78',
178
+ },
179
+ {
180
+ pattern: /\{\{\s*\$json\..*\}\}\s*[;&|`$]/,
181
+ name: 'Command Chaining via Expression',
182
+ type: 'command_injection',
183
+ severity: 'critical',
184
+ cwe: 'CWE-78',
185
+ },
186
+ {
187
+ pattern: /eval\s*\(\s*\{\{/,
188
+ name: 'Dynamic Code Evaluation',
189
+ type: 'command_injection',
190
+ severity: 'critical',
191
+ cwe: 'CWE-95',
192
+ },
193
+ {
194
+ pattern: /exec\s*\(\s*["'].*\{\{/,
195
+ name: 'exec() with user input',
196
+ type: 'command_injection',
197
+ severity: 'critical',
198
+ cwe: 'CWE-78',
199
+ },
200
+ {
201
+ pattern: /child_process.*spawn.*\{\{/,
202
+ name: 'Child process with user input',
203
+ type: 'command_injection',
204
+ severity: 'critical',
205
+ cwe: 'CWE-78',
206
+ },
207
+ // ============================================================================
208
+ // XSS Patterns
209
+ // ============================================================================
210
+ {
211
+ pattern: /\{\{\s*\$json\.[^}]+\}\}/,
212
+ name: 'Unescaped Template Expression',
213
+ type: 'xss',
214
+ severity: 'medium',
215
+ cwe: 'CWE-79',
216
+ },
217
+ {
218
+ pattern: /<script[^>]*>.*\{\{/i,
219
+ name: 'Script Tag with Expression',
220
+ type: 'xss',
221
+ severity: 'critical',
222
+ cwe: 'CWE-79',
223
+ },
224
+ {
225
+ pattern: /on\w+\s*=\s*["'][^"']*\{\{/i,
226
+ name: 'Event Handler with Expression',
227
+ type: 'xss',
228
+ severity: 'high',
229
+ cwe: 'CWE-79',
230
+ },
231
+ {
232
+ pattern: /javascript\s*:\s*.*\{\{/i,
233
+ name: 'javascript: URI with Expression',
234
+ type: 'xss',
235
+ severity: 'high',
236
+ cwe: 'CWE-79',
237
+ },
238
+ {
239
+ pattern: /innerHTML\s*=\s*.*\{\{/,
240
+ name: 'innerHTML with Expression',
241
+ type: 'xss',
242
+ severity: 'high',
243
+ cwe: 'CWE-79',
244
+ },
245
+ // ============================================================================
246
+ // Path Traversal Patterns
247
+ // ============================================================================
248
+ {
249
+ pattern: /\.\.\/.*\{\{/,
250
+ name: 'Path Traversal via Expression',
251
+ type: 'ssrf',
252
+ severity: 'high',
253
+ cwe: 'CWE-22',
254
+ },
255
+ {
256
+ pattern: /file:\/\/.*\{\{/,
257
+ name: 'Local File Access via Expression',
258
+ type: 'ssrf',
259
+ severity: 'critical',
260
+ cwe: 'CWE-22',
261
+ },
262
+ {
263
+ pattern: /\{\{\s*\$json\..*\}\}.*\.(txt|log|json|xml|csv|yml|yaml|env|config)/i,
264
+ name: 'Dynamic File Extension',
265
+ type: 'ssrf',
266
+ severity: 'medium',
267
+ cwe: 'CWE-22',
268
+ },
269
+ // ============================================================================
270
+ // SSRF Patterns
271
+ // ============================================================================
272
+ {
273
+ pattern: /https?:\/\/\{\{/,
274
+ name: 'Full URL from User Input',
275
+ type: 'ssrf',
276
+ severity: 'high',
277
+ cwe: 'CWE-918',
278
+ },
279
+ {
280
+ pattern: /https?:\/\/[^/]*\{\{\s*\$json/,
281
+ name: 'Dynamic Host in URL',
282
+ type: 'ssrf',
283
+ severity: 'critical',
284
+ cwe: 'CWE-918',
285
+ },
286
+ {
287
+ pattern: /localhost|127\.0\.0\.1|0\.0\.0\.0|::1/,
288
+ name: 'Internal IP Reference',
289
+ type: 'ssrf',
290
+ severity: 'medium',
291
+ cwe: 'CWE-918',
292
+ },
293
+ {
294
+ pattern: /169\.254\.169\.254/,
295
+ name: 'AWS Metadata URL',
296
+ type: 'ssrf',
297
+ severity: 'critical',
298
+ cwe: 'CWE-918',
299
+ },
300
+ // ============================================================================
301
+ // XML/XXE Patterns
302
+ // ============================================================================
303
+ {
304
+ pattern: /<!ENTITY\s+/i,
305
+ name: 'XML Entity Declaration',
306
+ type: 'xss',
307
+ severity: 'high',
308
+ cwe: 'CWE-611',
309
+ },
310
+ {
311
+ pattern: /<!DOCTYPE\s+.*\[/i,
312
+ name: 'XML DOCTYPE with DTD',
313
+ type: 'xss',
314
+ severity: 'medium',
315
+ cwe: 'CWE-611',
316
+ },
317
+ {
318
+ pattern: /SYSTEM\s+["']file:\/\//i,
319
+ name: 'XXE Local File Inclusion',
320
+ type: 'xss',
321
+ severity: 'critical',
322
+ cwe: 'CWE-611',
323
+ },
324
+ ];
325
+ class N8nSecurityAuditorAgent extends N8nBaseAgent_1.N8nBaseAgent {
326
+ constructor(config) {
327
+ const capabilities = [
328
+ {
329
+ name: 'secret-scanning',
330
+ version: '1.0.0',
331
+ description: 'Detect exposed secrets and credentials',
332
+ parameters: {},
333
+ },
334
+ {
335
+ name: 'injection-detection',
336
+ version: '1.0.0',
337
+ description: 'Detect SQL, command, and other injection risks',
338
+ parameters: {},
339
+ },
340
+ {
341
+ name: 'authentication-audit',
342
+ version: '1.0.0',
343
+ description: 'Audit authentication configurations',
344
+ parameters: {},
345
+ },
346
+ {
347
+ name: 'owasp-compliance',
348
+ version: '1.0.0',
349
+ description: 'Check OWASP Top 10 compliance',
350
+ parameters: {},
351
+ },
352
+ ];
353
+ super({
354
+ ...config,
355
+ type: 'n8n-security-auditor',
356
+ capabilities: [...capabilities, ...(config.capabilities || [])],
357
+ });
358
+ this.securityConfig = {
359
+ n8nConfig: config.n8nConfig,
360
+ secretPatterns: config.secretPatterns || SECRET_PATTERNS.map(p => p.pattern),
361
+ owaspChecks: config.owaspChecks || [
362
+ 'A01', 'A02', 'A03', 'A04', 'A05',
363
+ 'A06', 'A07', 'A08', 'A09', 'A10',
364
+ ],
365
+ severityThreshold: config.severityThreshold || 'low',
366
+ };
367
+ }
368
+ async performTask(task) {
369
+ const securityTask = task;
370
+ if (securityTask.type !== 'security-audit') {
371
+ throw new Error(`Unsupported task type: ${securityTask.type}`);
372
+ }
373
+ return this.auditWorkflow(securityTask.target, securityTask.options);
374
+ }
375
+ /**
376
+ * Perform full security audit on workflow
377
+ *
378
+ * PRODUCTION DEFAULT: Runtime checks are ENABLED by default.
379
+ * This ensures actual secret leakage detection, not just static analysis.
380
+ * Set executeRuntimeChecks: false to disable if workflow cannot be executed.
381
+ */
382
+ async auditWorkflow(workflowId, options) {
383
+ const workflow = await this.getWorkflow(workflowId);
384
+ const findings = [];
385
+ // Secret scanning (static analysis)
386
+ if (options?.checkSecrets !== false) {
387
+ findings.push(...this.scanForSecrets(workflow));
388
+ }
389
+ // Injection detection (static analysis)
390
+ if (options?.checkInjection !== false) {
391
+ findings.push(...this.detectInjectionRisks(workflow));
392
+ }
393
+ // Authentication audit (static analysis)
394
+ if (options?.checkAuthentication !== false) {
395
+ findings.push(...this.auditAuthentication(workflow));
396
+ }
397
+ // Runtime secret leakage detection - ENABLED BY DEFAULT
398
+ // This is critical for production - static analysis alone misses runtime leaks
399
+ // Set executeRuntimeChecks: false explicitly to skip
400
+ if (options?.executeRuntimeChecks !== false) {
401
+ try {
402
+ const runtimeFindings = await this.detectRuntimeSecretLeakage(workflowId, options?.testInput);
403
+ findings.push(...runtimeFindings);
404
+ }
405
+ catch (error) {
406
+ // If runtime execution fails, emit warning but continue with static results
407
+ this.emitEvent('security.runtime.skipped', {
408
+ workflowId,
409
+ reason: error instanceof Error ? error.message : 'Runtime execution failed',
410
+ note: 'Static analysis completed, but runtime leakage detection was skipped',
411
+ }, 'low');
412
+ }
413
+ }
414
+ // OWASP compliance
415
+ let owaspCompliance;
416
+ if (options?.checkOWASP !== false) {
417
+ owaspCompliance = this.checkOWASPCompliance(workflow, findings);
418
+ }
419
+ else {
420
+ owaspCompliance = { score: 0, categories: {} };
421
+ }
422
+ // Calculate risk score
423
+ const riskScore = this.calculateRiskScore(findings);
424
+ // Filter by severity threshold if specified
425
+ const filteredFindings = options?.severityThreshold
426
+ ? this.filterBySeverity(findings, options.severityThreshold)
427
+ : findings;
428
+ const result = {
429
+ workflowId,
430
+ workflowName: workflow.name,
431
+ auditDate: new Date().toISOString(),
432
+ riskScore,
433
+ findings: filteredFindings,
434
+ owaspCompliance,
435
+ summary: {
436
+ critical: findings.filter(f => f.severity === 'critical').length,
437
+ high: findings.filter(f => f.severity === 'high').length,
438
+ medium: findings.filter(f => f.severity === 'medium').length,
439
+ low: findings.filter(f => f.severity === 'low').length,
440
+ info: findings.filter(f => f.severity === 'info').length,
441
+ },
442
+ };
443
+ // Store result
444
+ await this.storeTestResult(`security-audit:${workflowId}`, result);
445
+ // Emit events for critical findings
446
+ if (result.summary.critical > 0) {
447
+ this.emitEvent('security.finding.critical', {
448
+ workflowId,
449
+ count: result.summary.critical,
450
+ findings: findings.filter(f => f.severity === 'critical'),
451
+ }, 'critical');
452
+ }
453
+ // Emit completion event
454
+ this.emitEvent('security.audit.completed', {
455
+ workflowId,
456
+ riskScore,
457
+ findingsCount: findings.length,
458
+ });
459
+ return result;
460
+ }
461
+ /**
462
+ * Scan workflow for exposed secrets
463
+ */
464
+ scanForSecrets(workflow) {
465
+ const findings = [];
466
+ let findingId = 1;
467
+ for (const node of workflow.nodes) {
468
+ const nodeJson = JSON.stringify(node.parameters);
469
+ for (const secretPattern of SECRET_PATTERNS) {
470
+ if (secretPattern.pattern.test(nodeJson)) {
471
+ findings.push({
472
+ id: `SEC-${String(findingId++).padStart(3, '0')}`,
473
+ type: 'hardcoded_secret',
474
+ severity: secretPattern.severity,
475
+ node: node.name,
476
+ message: `${secretPattern.name} detected in node parameters`,
477
+ details: `Found pattern matching ${secretPattern.name}. Hardcoded secrets should be moved to n8n credentials.`,
478
+ remediation: 'Move the secret to n8n credential store and reference it via credentials configuration.',
479
+ owaspCategory: 'A02',
480
+ cwe: 'CWE-798',
481
+ });
482
+ }
483
+ }
484
+ }
485
+ return findings;
486
+ }
487
+ /**
488
+ * Detect injection vulnerabilities
489
+ */
490
+ detectInjectionRisks(workflow) {
491
+ const findings = [];
492
+ let findingId = 100;
493
+ for (const node of workflow.nodes) {
494
+ // Check SQL nodes
495
+ if (node.type.includes('postgres') || node.type.includes('mysql') || node.type.includes('mssql')) {
496
+ const query = node.parameters.query;
497
+ if (query) {
498
+ for (const pattern of INJECTION_PATTERNS.filter(p => p.type === 'sql_injection')) {
499
+ if (pattern.pattern.test(query)) {
500
+ findings.push({
501
+ id: `SEC-${String(findingId++).padStart(3, '0')}`,
502
+ type: 'sql_injection',
503
+ severity: pattern.severity,
504
+ node: node.name,
505
+ message: `SQL injection vulnerability: ${pattern.name}`,
506
+ details: `Query uses string interpolation with user input. Attack vector: ${this.getSQLAttackVector(query)}`,
507
+ remediation: 'Use parameterized queries instead of string concatenation.',
508
+ owaspCategory: 'A03',
509
+ cwe: 'CWE-89',
510
+ });
511
+ }
512
+ }
513
+ }
514
+ }
515
+ // Check Execute Command nodes
516
+ if (node.type === 'n8n-nodes-base.executeCommand') {
517
+ const command = node.parameters.command;
518
+ if (command) {
519
+ if (/\{\{\s*\$json\./.test(command)) {
520
+ findings.push({
521
+ id: `SEC-${String(findingId++).padStart(3, '0')}`,
522
+ type: 'command_injection',
523
+ severity: 'critical',
524
+ node: node.name,
525
+ message: 'Command injection vulnerability',
526
+ details: `Command includes user-controlled input. Attack vector: User input could contain shell metacharacters.`,
527
+ remediation: 'Avoid using Execute Command with user input. If necessary, sanitize and escape all input.',
528
+ owaspCategory: 'A03',
529
+ cwe: 'CWE-78',
530
+ });
531
+ }
532
+ }
533
+ }
534
+ // Check HTTP Request nodes for SSRF
535
+ if (node.type === 'n8n-nodes-base.httpRequest') {
536
+ const url = node.parameters.url;
537
+ if (url && /\{\{\s*\$json\./.test(url)) {
538
+ findings.push({
539
+ id: `SEC-${String(findingId++).padStart(3, '0')}`,
540
+ type: 'ssrf',
541
+ severity: 'high',
542
+ node: node.name,
543
+ message: 'Potential SSRF vulnerability',
544
+ details: 'URL is constructed from user input, allowing potential Server-Side Request Forgery.',
545
+ remediation: 'Validate and whitelist allowed URLs. Do not allow user input to control request destination.',
546
+ owaspCategory: 'A10',
547
+ cwe: 'CWE-918',
548
+ });
549
+ }
550
+ // Check for insecure HTTP
551
+ if (url && url.startsWith('http://') && !url.includes('localhost') && !url.includes('127.0.0.1')) {
552
+ findings.push({
553
+ id: `SEC-${String(findingId++).padStart(3, '0')}`,
554
+ type: 'insecure_http',
555
+ severity: 'medium',
556
+ node: node.name,
557
+ message: 'Insecure HTTP connection',
558
+ details: `URL uses HTTP instead of HTTPS: ${url}`,
559
+ remediation: 'Use HTTPS for all external API calls.',
560
+ owaspCategory: 'A02',
561
+ cwe: 'CWE-319',
562
+ });
563
+ }
564
+ }
565
+ }
566
+ return findings;
567
+ }
568
+ /**
569
+ * Audit authentication configurations
570
+ */
571
+ auditAuthentication(workflow) {
572
+ const findings = [];
573
+ let findingId = 200;
574
+ for (const node of workflow.nodes) {
575
+ // Check webhook authentication
576
+ if (node.type.includes('webhook')) {
577
+ const auth = node.parameters.authentication;
578
+ if (!auth || auth === 'none') {
579
+ findings.push({
580
+ id: `SEC-${String(findingId++).padStart(3, '0')}`,
581
+ type: 'unauthenticated_webhook',
582
+ severity: 'high',
583
+ node: node.name,
584
+ message: 'Webhook has no authentication',
585
+ details: 'Anyone can trigger this webhook without credentials.',
586
+ remediation: 'Configure header authentication, basic auth, or JWT validation.',
587
+ owaspCategory: 'A01',
588
+ cwe: 'CWE-306',
589
+ });
590
+ }
591
+ }
592
+ // Check for basic auth over HTTP
593
+ if (node.type === 'n8n-nodes-base.httpRequest') {
594
+ const url = node.parameters.url;
595
+ const auth = node.parameters.authentication;
596
+ if (url?.startsWith('http://') && auth === 'basicAuth') {
597
+ findings.push({
598
+ id: `SEC-${String(findingId++).padStart(3, '0')}`,
599
+ type: 'weak_auth',
600
+ severity: 'high',
601
+ node: node.name,
602
+ message: 'Basic auth over insecure connection',
603
+ details: 'Basic authentication credentials sent over unencrypted HTTP.',
604
+ remediation: 'Use HTTPS when using basic authentication.',
605
+ owaspCategory: 'A02',
606
+ cwe: 'CWE-523',
607
+ });
608
+ }
609
+ }
610
+ }
611
+ return findings;
612
+ }
613
+ /**
614
+ * Check OWASP Top 10 compliance
615
+ */
616
+ checkOWASPCompliance(workflow, findings) {
617
+ const categories = {
618
+ 'A01_Broken_Access_Control': { status: 'pass', findings: 0 },
619
+ 'A02_Cryptographic_Failures': { status: 'pass', findings: 0 },
620
+ 'A03_Injection': { status: 'pass', findings: 0 },
621
+ 'A04_Insecure_Design': { status: 'pass', findings: 0 },
622
+ 'A05_Security_Misconfiguration': { status: 'pass', findings: 0 },
623
+ 'A06_Vulnerable_Components': { status: 'pass', findings: 0 },
624
+ 'A07_Auth_Failures': { status: 'pass', findings: 0 },
625
+ 'A08_Data_Integrity_Failures': { status: 'pass', findings: 0 },
626
+ 'A09_Logging_Monitoring_Failures': { status: 'pass', findings: 0 },
627
+ 'A10_SSRF': { status: 'pass', findings: 0 },
628
+ };
629
+ // Map findings to OWASP categories
630
+ const owaspMapping = {
631
+ 'A01': 'A01_Broken_Access_Control',
632
+ 'A02': 'A02_Cryptographic_Failures',
633
+ 'A03': 'A03_Injection',
634
+ 'A04': 'A04_Insecure_Design',
635
+ 'A05': 'A05_Security_Misconfiguration',
636
+ 'A06': 'A06_Vulnerable_Components',
637
+ 'A07': 'A07_Auth_Failures',
638
+ 'A08': 'A08_Data_Integrity_Failures',
639
+ 'A09': 'A09_Logging_Monitoring_Failures',
640
+ 'A10': 'A10_SSRF',
641
+ };
642
+ for (const finding of findings) {
643
+ if (finding.owaspCategory && owaspMapping[finding.owaspCategory]) {
644
+ const category = owaspMapping[finding.owaspCategory];
645
+ categories[category].findings++;
646
+ if (finding.severity === 'critical' || finding.severity === 'high') {
647
+ categories[category].status = 'fail';
648
+ }
649
+ else if (categories[category].status !== 'fail') {
650
+ categories[category].status = 'warn';
651
+ }
652
+ }
653
+ }
654
+ // Calculate compliance score
655
+ const totalCategories = Object.keys(categories).length;
656
+ const passedCategories = Object.values(categories).filter(c => c.status === 'pass').length;
657
+ const score = Math.round((passedCategories / totalCategories) * 100);
658
+ return { score, categories };
659
+ }
660
+ /**
661
+ * Calculate overall risk score
662
+ */
663
+ calculateRiskScore(findings) {
664
+ const weights = { critical: 25, high: 15, medium: 8, low: 3, info: 1 };
665
+ let totalPenalty = 0;
666
+ for (const finding of findings) {
667
+ totalPenalty += weights[finding.severity] || 0;
668
+ }
669
+ // Cap at 100
670
+ return Math.max(0, 100 - Math.min(totalPenalty, 100));
671
+ }
672
+ /**
673
+ * Filter findings by severity threshold
674
+ */
675
+ filterBySeverity(findings, threshold) {
676
+ const severityOrder = ['info', 'low', 'medium', 'high', 'critical'];
677
+ const thresholdIndex = severityOrder.indexOf(threshold);
678
+ return findings.filter(f => {
679
+ const findingIndex = severityOrder.indexOf(f.severity);
680
+ return findingIndex >= thresholdIndex;
681
+ });
682
+ }
683
+ /**
684
+ * Get example SQL attack vector
685
+ */
686
+ getSQLAttackVector(query) {
687
+ if (query.includes("'{{")) {
688
+ return "Input: ' OR '1'='1' -- would bypass authentication";
689
+ }
690
+ if (query.includes('" + $json')) {
691
+ return "Input containing SQL metacharacters could modify query logic";
692
+ }
693
+ return "User input could manipulate SQL query structure";
694
+ }
695
+ /**
696
+ * Quick security check (just critical and high issues)
697
+ */
698
+ async quickCheck(workflowId) {
699
+ const result = await this.auditWorkflow(workflowId, {
700
+ severityThreshold: 'high',
701
+ });
702
+ return {
703
+ secure: result.summary.critical === 0 && result.summary.high === 0,
704
+ criticalIssues: result.summary.critical,
705
+ highIssues: result.summary.high,
706
+ topIssue: result.findings[0]?.message || null,
707
+ };
708
+ }
709
+ /**
710
+ * Detect secret leakage at runtime by executing workflow and scanning output
711
+ * This catches secrets that are exposed through execution data, logs, or responses
712
+ */
713
+ async detectRuntimeSecretLeakage(workflowId, testInput) {
714
+ const findings = [];
715
+ let findingId = 300;
716
+ try {
717
+ // Execute workflow with test input
718
+ const execution = await this.executeWorkflow(workflowId, testInput || {}, {
719
+ waitForCompletion: true,
720
+ timeout: 30000,
721
+ });
722
+ // Wait for completion
723
+ const completedExecution = await this.waitForExecution(execution.id, 30000);
724
+ // Scan all execution output for secrets
725
+ const outputData = JSON.stringify(completedExecution.data || {});
726
+ for (const secretPattern of SECRET_PATTERNS) {
727
+ if (secretPattern.pattern.test(outputData)) {
728
+ // Find which node leaked the secret
729
+ const leakingNode = this.findLeakingNode(completedExecution, secretPattern.pattern);
730
+ findings.push({
731
+ id: `SEC-${String(findingId++).padStart(3, '0')}`,
732
+ type: 'secret_leakage',
733
+ severity: 'critical',
734
+ node: leakingNode || 'unknown',
735
+ message: `Runtime secret leakage detected: ${secretPattern.name}`,
736
+ details: `Secret of type "${secretPattern.name}" was found in workflow execution output. This may expose sensitive data to downstream systems or logs.`,
737
+ remediation: 'Ensure secrets are never passed through workflow data. Use n8n credentials and reference them directly in nodes.',
738
+ owaspCategory: 'A02',
739
+ cwe: 'CWE-200',
740
+ });
741
+ }
742
+ }
743
+ // Check for PII in output (email, phone, SSN patterns)
744
+ const piiPatterns = [
745
+ { pattern: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/, name: 'Email Address' },
746
+ { pattern: /\b\d{3}-\d{2}-\d{4}\b/, name: 'SSN' },
747
+ { pattern: /\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/, name: 'Phone Number' },
748
+ { pattern: /\b\d{16}\b/, name: 'Credit Card Number' },
749
+ ];
750
+ for (const pii of piiPatterns) {
751
+ if (pii.pattern.test(outputData)) {
752
+ findings.push({
753
+ id: `SEC-${String(findingId++).padStart(3, '0')}`,
754
+ type: 'pii_exposure',
755
+ severity: 'high',
756
+ node: 'workflow_output',
757
+ message: `PII detected in execution output: ${pii.name}`,
758
+ details: `Sensitive PII (${pii.name}) was found in workflow execution data.`,
759
+ remediation: 'Mask or redact PII before passing through workflow. Use data transformation nodes.',
760
+ owaspCategory: 'A02',
761
+ cwe: 'CWE-359',
762
+ });
763
+ }
764
+ }
765
+ // Check for secrets in error messages
766
+ const errorData = completedExecution.data?.resultData?.error;
767
+ if (errorData) {
768
+ const errorStr = JSON.stringify(errorData);
769
+ for (const secretPattern of SECRET_PATTERNS.slice(0, 10)) { // Check top patterns
770
+ if (secretPattern.pattern.test(errorStr)) {
771
+ findings.push({
772
+ id: `SEC-${String(findingId++).padStart(3, '0')}`,
773
+ type: 'secret_in_error',
774
+ severity: 'critical',
775
+ node: errorData.node || 'unknown',
776
+ message: `Secret exposed in error message: ${secretPattern.name}`,
777
+ details: 'Secrets should never appear in error messages as they may be logged or displayed.',
778
+ remediation: 'Catch errors and sanitize messages before propagation.',
779
+ owaspCategory: 'A02',
780
+ cwe: 'CWE-209',
781
+ });
782
+ }
783
+ }
784
+ }
785
+ }
786
+ catch (error) {
787
+ // Log but don't fail the entire audit
788
+ console.error('Runtime security check failed:', error);
789
+ }
790
+ return findings;
791
+ }
792
+ /**
793
+ * Find which node is leaking the secret
794
+ */
795
+ findLeakingNode(execution, pattern) {
796
+ const runData = execution.data?.resultData?.runData;
797
+ if (!runData)
798
+ return null;
799
+ for (const [nodeName, nodeRuns] of Object.entries(runData)) {
800
+ for (const run of nodeRuns) {
801
+ const nodeOutput = JSON.stringify(run.data || {});
802
+ if (pattern.test(nodeOutput)) {
803
+ return nodeName;
804
+ }
805
+ }
806
+ }
807
+ return null;
808
+ }
809
+ /**
810
+ * Wait for workflow execution to complete
811
+ */
812
+ async waitForExecution(executionId, timeoutMs) {
813
+ const startTime = Date.now();
814
+ while (Date.now() - startTime < timeoutMs) {
815
+ const execution = await this.getExecution(executionId);
816
+ if (execution.status !== 'running' && execution.status !== 'waiting') {
817
+ return execution;
818
+ }
819
+ await new Promise(resolve => setTimeout(resolve, 500));
820
+ }
821
+ throw new Error(`Execution ${executionId} timed out after ${timeoutMs}ms`);
822
+ }
823
+ }
824
+ exports.N8nSecurityAuditorAgent = N8nSecurityAuditorAgent;
825
+ //# sourceMappingURL=N8nSecurityAuditorAgent.js.map