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,986 @@
1
+ "use strict";
2
+ /**
3
+ * N8nComplianceValidatorAgent
4
+ *
5
+ * Regulatory and policy compliance validation:
6
+ * - GDPR compliance checking
7
+ * - Data retention policy validation
8
+ * - Audit trail verification
9
+ * - Privacy controls validation
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.N8nComplianceValidatorAgent = void 0;
13
+ const N8nBaseAgent_1 = require("./N8nBaseAgent");
14
+ // GDPR-specific patterns
15
+ const GDPR_CONTROLS = [
16
+ { id: 'GDPR-5.1', name: 'Lawfulness, fairness and transparency', category: 'principles' },
17
+ { id: 'GDPR-5.2', name: 'Purpose limitation', category: 'principles' },
18
+ { id: 'GDPR-5.3', name: 'Data minimization', category: 'principles' },
19
+ { id: 'GDPR-5.4', name: 'Accuracy', category: 'principles' },
20
+ { id: 'GDPR-5.5', name: 'Storage limitation', category: 'principles' },
21
+ { id: 'GDPR-5.6', name: 'Integrity and confidentiality', category: 'security' },
22
+ { id: 'GDPR-32', name: 'Security of processing', category: 'security' },
23
+ { id: 'GDPR-33', name: 'Data breach notification', category: 'incident' },
24
+ { id: 'GDPR-35', name: 'Data protection impact assessment', category: 'dpia' },
25
+ ];
26
+ // PII detection patterns
27
+ const PII_PATTERNS = {
28
+ email: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/,
29
+ phone: /\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/,
30
+ ssn: /\b\d{3}-\d{2}-\d{4}\b/,
31
+ creditCard: /\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/,
32
+ ipAddress: /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/,
33
+ name: /\b(first_?name|last_?name|full_?name|name)\b/i,
34
+ address: /\b(address|street|city|state|zip|postal)\b/i,
35
+ dob: /\b(dob|date_?of_?birth|birth_?date|birthday)\b/i,
36
+ };
37
+ class N8nComplianceValidatorAgent extends N8nBaseAgent_1.N8nBaseAgent {
38
+ constructor(config) {
39
+ const capabilities = [
40
+ {
41
+ name: 'gdpr-compliance',
42
+ version: '1.0.0',
43
+ description: 'GDPR compliance validation',
44
+ parameters: {},
45
+ },
46
+ {
47
+ name: 'data-classification',
48
+ version: '1.0.0',
49
+ description: 'Classify and detect sensitive data',
50
+ parameters: {},
51
+ },
52
+ {
53
+ name: 'audit-validation',
54
+ version: '1.0.0',
55
+ description: 'Validate audit trail configuration',
56
+ parameters: {},
57
+ },
58
+ {
59
+ name: 'policy-enforcement',
60
+ version: '1.0.0',
61
+ description: 'Enforce data handling policies',
62
+ parameters: {},
63
+ },
64
+ ];
65
+ super({
66
+ ...config,
67
+ type: 'n8n-compliance-validator',
68
+ capabilities: [...capabilities, ...(config.capabilities || [])],
69
+ });
70
+ }
71
+ async performTask(task) {
72
+ const complianceTask = task;
73
+ if (complianceTask.type !== 'compliance-validation') {
74
+ throw new Error(`Unsupported task type: ${complianceTask.type}`);
75
+ }
76
+ return this.validateCompliance(complianceTask.target, complianceTask.options);
77
+ }
78
+ /**
79
+ * Validate compliance for workflow
80
+ *
81
+ * PRODUCTION DEFAULT: Runtime PII tracing is ENABLED by default.
82
+ * This ensures actual data flow is analyzed, not just static configuration.
83
+ * Set executeRuntimeTracing: false to disable if workflow cannot be executed.
84
+ */
85
+ async validateCompliance(workflowId, options) {
86
+ const workflow = await this.getWorkflow(workflowId);
87
+ const frameworks = options?.frameworks || ['GDPR'];
88
+ // Validate each framework (static analysis)
89
+ const frameworkResults = [];
90
+ for (const framework of frameworks) {
91
+ const result = this.validateFramework(workflow, framework);
92
+ frameworkResults.push(result);
93
+ }
94
+ // Check data handling (static analysis)
95
+ const dataHandling = options?.checkDataHandling !== false
96
+ ? this.validateDataHandling(workflow)
97
+ : this.getDefaultDataHandling();
98
+ // Check audit trail (static analysis)
99
+ const auditTrail = options?.checkAuditTrail !== false
100
+ ? this.validateAuditTrail(workflow)
101
+ : this.getDefaultAuditTrail();
102
+ // Check retention policy (static analysis)
103
+ const retentionPolicy = options?.checkRetention !== false
104
+ ? this.validateRetentionPolicy(workflow)
105
+ : this.getDefaultRetentionPolicy();
106
+ // Runtime PII flow tracing - ENABLED BY DEFAULT
107
+ // This is critical for production - static analysis alone misses runtime data flows
108
+ // Set executeRuntimeTracing: false explicitly to skip
109
+ let runtimeFindings = [];
110
+ if (options?.executeRuntimeTracing !== false) {
111
+ try {
112
+ runtimeFindings = await this.executeRuntimePIITracing(workflowId, options?.testInput);
113
+ }
114
+ catch (error) {
115
+ // If runtime execution fails, emit warning but continue with static results
116
+ this.emitEvent('compliance.runtime.skipped', {
117
+ workflowId,
118
+ reason: error instanceof Error ? error.message : 'Runtime PII tracing failed',
119
+ note: 'Static compliance analysis completed, but runtime PII flow tracing was skipped',
120
+ });
121
+ }
122
+ }
123
+ // Collect all findings
124
+ const findings = [
125
+ ...frameworkResults.flatMap(f => f.findings),
126
+ ...this.dataHandlingFindings(dataHandling),
127
+ ...this.auditTrailFindings(auditTrail),
128
+ ...runtimeFindings,
129
+ ...this.retentionFindings(retentionPolicy),
130
+ ];
131
+ // Calculate overall compliance
132
+ const overallCompliance = this.calculateOverallCompliance(frameworkResults, findings);
133
+ // Generate remediation plan
134
+ const remediationPlan = this.generateRemediationPlan(findings);
135
+ const result = {
136
+ workflowId,
137
+ timestamp: new Date(),
138
+ frameworks: frameworkResults,
139
+ dataHandling,
140
+ auditTrail,
141
+ retentionPolicy,
142
+ overallCompliance,
143
+ findings,
144
+ remediationPlan,
145
+ };
146
+ // Store result
147
+ await this.storeTestResult(`compliance:${workflowId}`, result);
148
+ // Emit event
149
+ this.emitEvent('compliance.validation.completed', {
150
+ workflowId,
151
+ isCompliant: overallCompliance.isCompliant,
152
+ score: overallCompliance.score,
153
+ findingCount: findings.length,
154
+ });
155
+ return result;
156
+ }
157
+ /**
158
+ * Validate compliance for a specific framework
159
+ */
160
+ validateFramework(workflow, framework) {
161
+ switch (framework) {
162
+ case 'GDPR':
163
+ return this.validateGDPR(workflow);
164
+ case 'HIPAA':
165
+ return this.validateHIPAA(workflow);
166
+ case 'SOC2':
167
+ return this.validateSOC2(workflow);
168
+ case 'PCI-DSS':
169
+ return this.validatePCIDSS(workflow);
170
+ default:
171
+ return this.getDefaultFrameworkResult(framework);
172
+ }
173
+ }
174
+ /**
175
+ * Validate GDPR compliance
176
+ */
177
+ validateGDPR(workflow) {
178
+ const controls = [];
179
+ const findings = [];
180
+ // Check each GDPR control
181
+ for (const control of GDPR_CONTROLS) {
182
+ const result = this.checkGDPRControl(workflow, control);
183
+ controls.push(result);
184
+ if (result.status !== 'compliant' && result.status !== 'not-applicable') {
185
+ findings.push({
186
+ id: `GDPR-${control.id}-${Date.now()}`,
187
+ framework: 'GDPR',
188
+ severity: result.status === 'non-compliant' ? 'error' : 'warning',
189
+ category: control.category,
190
+ title: `${control.name} - ${result.status}`,
191
+ description: result.gaps.join('; '),
192
+ location: 'workflow',
193
+ evidence: result.evidence.join(', '),
194
+ remediation: this.getGDPRRemediation(control.id),
195
+ });
196
+ }
197
+ }
198
+ // Check for international data transfers
199
+ const transfers = this.detectDataTransfers(workflow);
200
+ if (transfers.length > 0) {
201
+ findings.push({
202
+ id: `GDPR-transfer-${Date.now()}`,
203
+ framework: 'GDPR',
204
+ severity: 'warning',
205
+ category: 'transfer',
206
+ title: 'International data transfer detected',
207
+ description: `Data may be transferred to: ${transfers.join(', ')}`,
208
+ location: 'workflow',
209
+ evidence: 'HTTP requests to external services',
210
+ remediation: 'Ensure appropriate safeguards for international transfers',
211
+ });
212
+ }
213
+ const compliantCount = controls.filter(c => c.status === 'compliant').length;
214
+ const score = (compliantCount / controls.length) * 100;
215
+ return {
216
+ framework: 'GDPR',
217
+ isCompliant: findings.filter(f => f.severity === 'error' || f.severity === 'critical').length === 0,
218
+ score,
219
+ controls,
220
+ findings,
221
+ };
222
+ }
223
+ /**
224
+ * Check specific GDPR control
225
+ */
226
+ checkGDPRControl(workflow, control) {
227
+ const evidence = [];
228
+ const gaps = [];
229
+ switch (control.id) {
230
+ case 'GDPR-5.1': // Lawfulness
231
+ if (this.hasLawfulBasisDocumentation(workflow)) {
232
+ evidence.push('Processing purposes documented');
233
+ }
234
+ else {
235
+ gaps.push('No documented lawful basis for processing');
236
+ }
237
+ break;
238
+ case 'GDPR-5.3': // Data minimization
239
+ const unnecessaryData = this.detectUnnecessaryData(workflow);
240
+ if (unnecessaryData.length === 0) {
241
+ evidence.push('No unnecessary data collection detected');
242
+ }
243
+ else {
244
+ gaps.push(`Potentially unnecessary data: ${unnecessaryData.join(', ')}`);
245
+ }
246
+ break;
247
+ case 'GDPR-5.5': // Storage limitation
248
+ if (workflow.settings?.saveDataSuccessExecution === 'none') {
249
+ evidence.push('Data not retained after execution');
250
+ }
251
+ else {
252
+ gaps.push('Data may be retained indefinitely');
253
+ }
254
+ break;
255
+ case 'GDPR-5.6': // Security
256
+ const securityIssues = this.checkSecurityControls(workflow);
257
+ if (securityIssues.length === 0) {
258
+ evidence.push('Security controls in place');
259
+ }
260
+ else {
261
+ gaps.push(...securityIssues);
262
+ }
263
+ break;
264
+ case 'GDPR-32': // Security of processing
265
+ if (this.hasEncryption(workflow)) {
266
+ evidence.push('Encryption configured for sensitive data');
267
+ }
268
+ else {
269
+ gaps.push('No encryption detected for data in transit');
270
+ }
271
+ break;
272
+ default:
273
+ evidence.push('Control not specifically validated');
274
+ }
275
+ let status;
276
+ if (gaps.length === 0 && evidence.length > 0) {
277
+ status = 'compliant';
278
+ }
279
+ else if (gaps.length > 0 && evidence.length > 0) {
280
+ status = 'partial';
281
+ }
282
+ else if (gaps.length > 0) {
283
+ status = 'non-compliant';
284
+ }
285
+ else {
286
+ status = 'not-applicable';
287
+ }
288
+ return {
289
+ controlId: control.id,
290
+ controlName: control.name,
291
+ status,
292
+ evidence,
293
+ gaps,
294
+ };
295
+ }
296
+ /**
297
+ * Get GDPR remediation guidance
298
+ */
299
+ getGDPRRemediation(controlId) {
300
+ const remediations = {
301
+ 'GDPR-5.1': 'Document the lawful basis for processing in workflow metadata',
302
+ 'GDPR-5.2': 'Ensure data is only used for documented purposes',
303
+ 'GDPR-5.3': 'Remove unnecessary data fields from processing',
304
+ 'GDPR-5.4': 'Implement data validation and accuracy checks',
305
+ 'GDPR-5.5': 'Configure data retention policies and automatic deletion',
306
+ 'GDPR-5.6': 'Enable encryption and access controls',
307
+ 'GDPR-32': 'Implement appropriate technical security measures',
308
+ 'GDPR-33': 'Configure breach notification workflow',
309
+ 'GDPR-35': 'Complete DPIA for high-risk processing',
310
+ };
311
+ return remediations[controlId] || 'Review and address the identified gap';
312
+ }
313
+ /**
314
+ * Validate HIPAA compliance
315
+ */
316
+ validateHIPAA(workflow) {
317
+ const findings = [];
318
+ const controls = [];
319
+ // Check for PHI handling
320
+ const phiDetected = this.detectPHI(workflow);
321
+ if (phiDetected.length > 0) {
322
+ // Check encryption
323
+ if (!this.hasEncryption(workflow)) {
324
+ findings.push({
325
+ id: `HIPAA-encryption-${Date.now()}`,
326
+ framework: 'HIPAA',
327
+ severity: 'critical',
328
+ category: 'security',
329
+ title: 'PHI transmitted without encryption',
330
+ description: 'Protected Health Information detected without encryption',
331
+ location: phiDetected.join(', '),
332
+ evidence: 'PHI fields detected in workflow',
333
+ remediation: 'Enable encryption for all PHI data',
334
+ });
335
+ }
336
+ // Check access controls
337
+ if (!this.hasAccessControls(workflow)) {
338
+ findings.push({
339
+ id: `HIPAA-access-${Date.now()}`,
340
+ framework: 'HIPAA',
341
+ severity: 'error',
342
+ category: 'access',
343
+ title: 'Inadequate access controls for PHI',
344
+ description: 'No access controls detected for PHI handling',
345
+ location: 'workflow',
346
+ evidence: 'Workflow has no credential restrictions',
347
+ remediation: 'Implement role-based access controls',
348
+ });
349
+ }
350
+ }
351
+ controls.push({
352
+ controlId: 'HIPAA-164.312(a)',
353
+ controlName: 'Access Control',
354
+ status: this.hasAccessControls(workflow) ? 'compliant' : 'non-compliant',
355
+ evidence: [],
356
+ gaps: [],
357
+ });
358
+ controls.push({
359
+ controlId: 'HIPAA-164.312(e)',
360
+ controlName: 'Transmission Security',
361
+ status: this.hasEncryption(workflow) ? 'compliant' : 'non-compliant',
362
+ evidence: [],
363
+ gaps: [],
364
+ });
365
+ return {
366
+ framework: 'HIPAA',
367
+ isCompliant: findings.filter(f => f.severity === 'critical').length === 0,
368
+ score: controls.filter(c => c.status === 'compliant').length / controls.length * 100,
369
+ controls,
370
+ findings,
371
+ };
372
+ }
373
+ /**
374
+ * Validate SOC2 compliance
375
+ */
376
+ validateSOC2(workflow) {
377
+ const findings = [];
378
+ const controls = [];
379
+ // Trust Service Criteria checks
380
+ const criteria = [
381
+ { id: 'CC6.1', name: 'Logical and Physical Access', check: () => this.hasAccessControls(workflow) },
382
+ { id: 'CC6.6', name: 'System Boundaries', check: () => this.hasNetworkControls(workflow) },
383
+ { id: 'CC7.2', name: 'Change Management', check: () => this.hasChangeControls(workflow) },
384
+ { id: 'CC8.1', name: 'Availability', check: () => this.hasAvailabilityControls(workflow) },
385
+ ];
386
+ for (const criterion of criteria) {
387
+ const passed = criterion.check();
388
+ controls.push({
389
+ controlId: criterion.id,
390
+ controlName: criterion.name,
391
+ status: passed ? 'compliant' : 'partial',
392
+ evidence: [],
393
+ gaps: passed ? [] : [`${criterion.name} controls not fully implemented`],
394
+ });
395
+ if (!passed) {
396
+ findings.push({
397
+ id: `SOC2-${criterion.id}-${Date.now()}`,
398
+ framework: 'SOC2',
399
+ severity: 'warning',
400
+ category: 'trust-criteria',
401
+ title: `${criterion.name} - Partial compliance`,
402
+ description: `${criterion.name} controls may need enhancement`,
403
+ location: 'workflow',
404
+ evidence: 'Automated check',
405
+ remediation: `Review and enhance ${criterion.name.toLowerCase()} controls`,
406
+ });
407
+ }
408
+ }
409
+ return {
410
+ framework: 'SOC2',
411
+ isCompliant: findings.filter(f => f.severity === 'error' || f.severity === 'critical').length === 0,
412
+ score: controls.filter(c => c.status === 'compliant').length / controls.length * 100,
413
+ controls,
414
+ findings,
415
+ };
416
+ }
417
+ /**
418
+ * Validate PCI-DSS compliance
419
+ */
420
+ validatePCIDSS(workflow) {
421
+ const findings = [];
422
+ const controls = [];
423
+ // Check for payment card data
424
+ const cardDataNodes = this.detectCardData(workflow);
425
+ if (cardDataNodes.length > 0) {
426
+ // Requirement 3: Protect stored cardholder data
427
+ if (!this.hasCardDataProtection(workflow)) {
428
+ findings.push({
429
+ id: `PCI-req3-${Date.now()}`,
430
+ framework: 'PCI-DSS',
431
+ severity: 'critical',
432
+ category: 'data-protection',
433
+ title: 'Cardholder data not protected',
434
+ description: 'Payment card data detected without adequate protection',
435
+ location: cardDataNodes.join(', '),
436
+ evidence: 'Card data patterns detected',
437
+ remediation: 'Encrypt or mask cardholder data',
438
+ });
439
+ }
440
+ // Requirement 4: Encrypt transmission
441
+ if (!this.hasEncryption(workflow)) {
442
+ findings.push({
443
+ id: `PCI-req4-${Date.now()}`,
444
+ framework: 'PCI-DSS',
445
+ severity: 'critical',
446
+ category: 'encryption',
447
+ title: 'Card data transmitted without encryption',
448
+ description: 'Payment data may be transmitted unencrypted',
449
+ location: 'workflow',
450
+ evidence: 'No TLS/encryption detected',
451
+ remediation: 'Enable TLS for all cardholder data transmission',
452
+ });
453
+ }
454
+ }
455
+ controls.push({
456
+ controlId: 'PCI-3',
457
+ controlName: 'Protect Stored Data',
458
+ status: cardDataNodes.length === 0 || this.hasCardDataProtection(workflow) ? 'compliant' : 'non-compliant',
459
+ evidence: [],
460
+ gaps: [],
461
+ });
462
+ controls.push({
463
+ controlId: 'PCI-4',
464
+ controlName: 'Encrypt Transmission',
465
+ status: this.hasEncryption(workflow) ? 'compliant' : 'non-compliant',
466
+ evidence: [],
467
+ gaps: [],
468
+ });
469
+ return {
470
+ framework: 'PCI-DSS',
471
+ isCompliant: findings.filter(f => f.severity === 'critical').length === 0,
472
+ score: controls.filter(c => c.status === 'compliant').length / controls.length * 100,
473
+ controls,
474
+ findings,
475
+ };
476
+ }
477
+ /**
478
+ * Validate data handling practices
479
+ */
480
+ validateDataHandling(workflow) {
481
+ const categories = [];
482
+ const risks = [];
483
+ // Detect PII
484
+ const piiFields = this.detectPII(workflow);
485
+ if (piiFields.length > 0) {
486
+ categories.push({
487
+ type: 'PII',
488
+ fields: piiFields,
489
+ nodes: this.findNodesWithFields(workflow, piiFields),
490
+ protection: this.hasEncryption(workflow) ? 'encrypted' : 'none',
491
+ });
492
+ if (!this.hasEncryption(workflow)) {
493
+ risks.push({
494
+ type: 'unprotected-pii',
495
+ severity: 'high',
496
+ description: 'PII data may be processed without encryption',
497
+ mitigation: 'Enable encryption for PII fields',
498
+ });
499
+ }
500
+ }
501
+ // Detect financial data
502
+ const financialFields = this.detectFinancialData(workflow);
503
+ if (financialFields.length > 0) {
504
+ categories.push({
505
+ type: 'financial',
506
+ fields: financialFields,
507
+ nodes: this.findNodesWithFields(workflow, financialFields),
508
+ protection: this.hasEncryption(workflow) ? 'encrypted' : 'none',
509
+ });
510
+ }
511
+ // Processing activities
512
+ const processingActivities = [{
513
+ purpose: workflow.settings?.description || 'Automated data processing',
514
+ dataSubjects: 'Unknown',
515
+ transfers: this.detectDataTransfers(workflow),
516
+ }];
517
+ return {
518
+ personalDataDetected: piiFields.length > 0,
519
+ sensitiveDataDetected: financialFields.length > 0,
520
+ dataCategories: categories,
521
+ processingActivities,
522
+ risks,
523
+ };
524
+ }
525
+ /**
526
+ * Validate audit trail
527
+ */
528
+ validateAuditTrail(workflow) {
529
+ const gaps = [];
530
+ const recommendations = [];
531
+ // Check execution logging
532
+ const hasExecutionLogs = workflow.settings?.saveExecutionProgress === true;
533
+ if (!hasExecutionLogs) {
534
+ gaps.push('Execution progress not logged');
535
+ recommendations.push('Enable saveExecutionProgress for audit trail');
536
+ }
537
+ // Check error logging
538
+ const hasErrorLogs = workflow.settings?.saveDataErrorExecution !== 'none';
539
+ if (!hasErrorLogs) {
540
+ gaps.push('Error execution data not saved');
541
+ recommendations.push('Enable error execution data saving');
542
+ }
543
+ // Calculate coverage
544
+ const coverage = ((hasExecutionLogs ? 50 : 0) + (hasErrorLogs ? 50 : 0));
545
+ return {
546
+ isEnabled: hasExecutionLogs || hasErrorLogs,
547
+ coverage,
548
+ gaps,
549
+ recommendations,
550
+ };
551
+ }
552
+ /**
553
+ * Validate retention policy
554
+ */
555
+ validateRetentionPolicy(workflow) {
556
+ const policies = [];
557
+ const violations = [];
558
+ // Check workflow-level retention
559
+ const saveDataSuccessExecution = workflow.settings?.saveDataSuccessExecution;
560
+ const saveDataErrorExecution = workflow.settings?.saveDataErrorExecution;
561
+ if (saveDataSuccessExecution === 'all') {
562
+ violations.push('All successful execution data retained indefinitely');
563
+ }
564
+ policies.push({
565
+ dataType: 'successful-executions',
566
+ retentionPeriod: saveDataSuccessExecution === 'none' ? '0 days' :
567
+ saveDataSuccessExecution === 'lastSuccess' ? 'Last execution only' : 'Indefinite',
568
+ deletionMethod: 'automatic',
569
+ isEnforced: saveDataSuccessExecution !== 'all',
570
+ });
571
+ policies.push({
572
+ dataType: 'error-executions',
573
+ retentionPeriod: saveDataErrorExecution === 'none' ? '0 days' : 'Indefinite',
574
+ deletionMethod: 'manual',
575
+ isEnforced: saveDataErrorExecution === 'none',
576
+ });
577
+ return {
578
+ hasPolicy: policies.some(p => p.isEnforced),
579
+ policies,
580
+ violations,
581
+ };
582
+ }
583
+ // Helper methods
584
+ detectPII(workflow) {
585
+ const piiFields = [];
586
+ const workflowStr = JSON.stringify(workflow).toLowerCase();
587
+ for (const [name, pattern] of Object.entries(PII_PATTERNS)) {
588
+ if (pattern.test(workflowStr)) {
589
+ piiFields.push(name);
590
+ }
591
+ }
592
+ return piiFields;
593
+ }
594
+ detectPHI(workflow) {
595
+ const phiPatterns = [
596
+ /medical|health|diagnosis|treatment|prescription/i,
597
+ /patient|doctor|hospital|clinic/i,
598
+ /insurance|claim|coverage/i,
599
+ ];
600
+ const workflowStr = JSON.stringify(workflow);
601
+ const detected = [];
602
+ for (const pattern of phiPatterns) {
603
+ if (pattern.test(workflowStr)) {
604
+ detected.push(pattern.source);
605
+ }
606
+ }
607
+ return detected;
608
+ }
609
+ detectFinancialData(workflow) {
610
+ const patterns = [
611
+ /account.*number|routing.*number/i,
612
+ /credit.*card|debit.*card/i,
613
+ /bank|payment|transaction/i,
614
+ ];
615
+ const workflowStr = JSON.stringify(workflow);
616
+ const detected = [];
617
+ for (const pattern of patterns) {
618
+ if (pattern.test(workflowStr)) {
619
+ detected.push(pattern.source);
620
+ }
621
+ }
622
+ return detected;
623
+ }
624
+ detectCardData(workflow) {
625
+ const nodes = [];
626
+ for (const node of workflow.nodes) {
627
+ const nodeStr = JSON.stringify(node.parameters);
628
+ if (PII_PATTERNS.creditCard.test(nodeStr) || /card_?number|cvv|expir/i.test(nodeStr)) {
629
+ nodes.push(node.name);
630
+ }
631
+ }
632
+ return nodes;
633
+ }
634
+ detectDataTransfers(workflow) {
635
+ const transfers = [];
636
+ for (const node of workflow.nodes) {
637
+ if (node.type.includes('httpRequest')) {
638
+ const url = node.parameters.url;
639
+ if (url) {
640
+ try {
641
+ const hostname = new URL(url).hostname;
642
+ if (!hostname.includes('localhost') && !hostname.includes('127.0.0.1')) {
643
+ transfers.push(hostname);
644
+ }
645
+ }
646
+ catch {
647
+ // Invalid URL
648
+ }
649
+ }
650
+ }
651
+ }
652
+ return [...new Set(transfers)];
653
+ }
654
+ hasLawfulBasisDocumentation(workflow) {
655
+ return !!(workflow.settings?.description || workflow.tags?.length);
656
+ }
657
+ detectUnnecessaryData(workflow) {
658
+ // Check for overly broad data selection
659
+ const unnecessary = [];
660
+ for (const node of workflow.nodes) {
661
+ if (node.type.includes('postgres') || node.type.includes('mysql')) {
662
+ const query = node.parameters.query;
663
+ if (query && query.includes('SELECT *')) {
664
+ unnecessary.push(`${node.name}: SELECT * may retrieve unnecessary data`);
665
+ }
666
+ }
667
+ }
668
+ return unnecessary;
669
+ }
670
+ checkSecurityControls(workflow) {
671
+ const issues = [];
672
+ // Check for hardcoded credentials
673
+ for (const node of workflow.nodes) {
674
+ const paramsStr = JSON.stringify(node.parameters);
675
+ if (/password\s*[=:]\s*["'][^"']+["']/i.test(paramsStr)) {
676
+ issues.push(`Hardcoded password in ${node.name}`);
677
+ }
678
+ }
679
+ return issues;
680
+ }
681
+ hasEncryption(workflow) {
682
+ // Check if HTTPS is used for HTTP requests
683
+ for (const node of workflow.nodes) {
684
+ if (node.type.includes('httpRequest')) {
685
+ const url = node.parameters.url;
686
+ if (url && url.startsWith('http://')) {
687
+ return false;
688
+ }
689
+ }
690
+ }
691
+ return true;
692
+ }
693
+ hasAccessControls(workflow) {
694
+ // Check for credential usage (indicates access control)
695
+ return workflow.nodes.some(n => n.credentials && Object.keys(n.credentials).length > 0);
696
+ }
697
+ hasNetworkControls(workflow) {
698
+ return true; // Would need n8n instance configuration
699
+ }
700
+ hasChangeControls(workflow) {
701
+ return !!(workflow.versionId);
702
+ }
703
+ hasAvailabilityControls(workflow) {
704
+ return !!(workflow.settings?.errorWorkflow);
705
+ }
706
+ hasCardDataProtection(workflow) {
707
+ return this.hasEncryption(workflow);
708
+ }
709
+ findNodesWithFields(workflow, fields) {
710
+ const nodes = [];
711
+ const fieldsRegex = new RegExp(fields.join('|'), 'i');
712
+ for (const node of workflow.nodes) {
713
+ if (fieldsRegex.test(JSON.stringify(node.parameters))) {
714
+ nodes.push(node.name);
715
+ }
716
+ }
717
+ return nodes;
718
+ }
719
+ dataHandlingFindings(data) {
720
+ return data.risks.map((risk, i) => ({
721
+ id: `data-risk-${i}`,
722
+ severity: risk.severity,
723
+ category: 'data-handling',
724
+ title: risk.type,
725
+ description: risk.description,
726
+ location: 'workflow',
727
+ evidence: 'Data analysis',
728
+ remediation: risk.mitigation,
729
+ }));
730
+ }
731
+ auditTrailFindings(audit) {
732
+ return audit.gaps.map((gap, i) => ({
733
+ id: `audit-gap-${i}`,
734
+ severity: 'warning',
735
+ category: 'audit',
736
+ title: 'Audit trail gap',
737
+ description: gap,
738
+ location: 'workflow',
739
+ evidence: 'Configuration check',
740
+ remediation: audit.recommendations[i] || 'Enable audit logging',
741
+ }));
742
+ }
743
+ retentionFindings(retention) {
744
+ return retention.violations.map((v, i) => ({
745
+ id: `retention-${i}`,
746
+ severity: 'warning',
747
+ category: 'retention',
748
+ title: 'Retention policy issue',
749
+ description: v,
750
+ location: 'workflow',
751
+ evidence: 'Policy check',
752
+ remediation: 'Configure appropriate retention policy',
753
+ }));
754
+ }
755
+ calculateOverallCompliance(frameworks, findings) {
756
+ const avgScore = frameworks.reduce((sum, f) => sum + f.score, 0) / frameworks.length;
757
+ const criticalCount = findings.filter(f => f.severity === 'critical').length;
758
+ const errorCount = findings.filter(f => f.severity === 'error').length;
759
+ let riskLevel;
760
+ if (criticalCount > 0)
761
+ riskLevel = 'critical';
762
+ else if (errorCount > 2)
763
+ riskLevel = 'high';
764
+ else if (errorCount > 0 || avgScore < 70)
765
+ riskLevel = 'medium';
766
+ else
767
+ riskLevel = 'low';
768
+ return {
769
+ isCompliant: criticalCount === 0 && errorCount === 0,
770
+ score: avgScore,
771
+ riskLevel,
772
+ };
773
+ }
774
+ generateRemediationPlan(findings) {
775
+ const priorityMap = {
776
+ critical: 1,
777
+ error: 2,
778
+ warning: 3,
779
+ info: 4,
780
+ };
781
+ return findings
782
+ .sort((a, b) => priorityMap[a.severity] - priorityMap[b.severity])
783
+ .map((finding, i) => ({
784
+ findingId: finding.id,
785
+ priority: i + 1,
786
+ effort: finding.severity === 'critical' ? 'high' : finding.severity === 'error' ? 'medium' : 'low',
787
+ action: finding.remediation,
788
+ }));
789
+ }
790
+ getDefaultFrameworkResult(framework) {
791
+ return {
792
+ framework,
793
+ isCompliant: true,
794
+ score: 100,
795
+ controls: [],
796
+ findings: [],
797
+ };
798
+ }
799
+ getDefaultDataHandling() {
800
+ return {
801
+ personalDataDetected: false,
802
+ sensitiveDataDetected: false,
803
+ dataCategories: [],
804
+ processingActivities: [],
805
+ risks: [],
806
+ };
807
+ }
808
+ getDefaultAuditTrail() {
809
+ return {
810
+ isEnabled: false,
811
+ coverage: 0,
812
+ gaps: [],
813
+ recommendations: [],
814
+ };
815
+ }
816
+ getDefaultRetentionPolicy() {
817
+ return {
818
+ hasPolicy: false,
819
+ policies: [],
820
+ violations: [],
821
+ };
822
+ }
823
+ /**
824
+ * Execute workflow and trace PII data flow through nodes
825
+ * This catches runtime compliance issues like PII being logged,
826
+ * sent to unauthorized destinations, or retained improperly
827
+ */
828
+ async executeRuntimePIITracing(workflowId, testInput) {
829
+ const findings = [];
830
+ // Generate test data with identifiable PII for tracing
831
+ const piiTestData = {
832
+ email: 'test.trace@pii-detection.test',
833
+ phone: '555-123-4567',
834
+ ssn: '123-45-6789',
835
+ name: 'PII_Trace_Name',
836
+ address: '123 PII Trace Street',
837
+ creditCard: '4111111111111111',
838
+ ...testInput,
839
+ };
840
+ try {
841
+ // Execute workflow with PII test data
842
+ const execution = await this.executeWorkflow(workflowId, piiTestData, {
843
+ waitForCompletion: true,
844
+ timeout: 30000,
845
+ });
846
+ // Wait for completion
847
+ const completedExecution = await this.waitForExecution(execution.id, 30000);
848
+ // Trace PII through each node's output
849
+ const piiTrace = this.tracePIIFlow(completedExecution, piiTestData);
850
+ // Analyze the trace for compliance issues
851
+ for (const [nodeName, piiFound] of Object.entries(piiTrace)) {
852
+ if (piiFound.length > 0) {
853
+ // Check if this node should have PII
854
+ const isExpectedPIINode = this.isAuthorizedPIINode(nodeName, completedExecution);
855
+ if (!isExpectedPIINode) {
856
+ findings.push({
857
+ id: `runtime-pii-${nodeName}-${Date.now()}`,
858
+ severity: 'error',
859
+ category: 'data-flow',
860
+ title: `Unauthorized PII flow detected in ${nodeName}`,
861
+ description: `PII types found: ${piiFound.join(', ')}. This node may be processing PII without authorization.`,
862
+ location: nodeName,
863
+ evidence: `Traced PII: ${piiFound.join(', ')}`,
864
+ remediation: 'Review data flow and ensure PII is only processed in authorized nodes with proper safeguards.',
865
+ });
866
+ }
867
+ // Check if PII is being sent to external services
868
+ if (this.isExternalServiceNode(nodeName, completedExecution)) {
869
+ findings.push({
870
+ id: `runtime-pii-external-${nodeName}-${Date.now()}`,
871
+ framework: 'GDPR',
872
+ severity: 'critical',
873
+ category: 'data-transfer',
874
+ title: `PII potentially transferred externally via ${nodeName}`,
875
+ description: `PII (${piiFound.join(', ')}) detected in data sent to external service.`,
876
+ location: nodeName,
877
+ evidence: `External node with PII: ${piiFound.join(', ')}`,
878
+ remediation: 'Ensure data processing agreement exists with third party. Consider data minimization.',
879
+ });
880
+ }
881
+ }
882
+ }
883
+ // Check if PII was stored in execution logs
884
+ if (completedExecution.data?.resultData?.runData) {
885
+ const executionDataStr = JSON.stringify(completedExecution.data);
886
+ for (const [piiType, piiValue] of Object.entries(piiTestData)) {
887
+ if (typeof piiValue === 'string' && executionDataStr.includes(piiValue)) {
888
+ // PII found in execution data - check retention settings
889
+ const workflow = await this.getWorkflow(workflowId);
890
+ if (workflow.settings?.saveDataSuccessExecution === 'all') {
891
+ findings.push({
892
+ id: `runtime-pii-retention-${piiType}-${Date.now()}`,
893
+ framework: 'GDPR',
894
+ severity: 'error', // Use 'error' instead of 'high' per type definition
895
+ category: 'retention',
896
+ title: `PII (${piiType}) may be retained in execution logs`,
897
+ description: `PII data found in execution results which are configured to be retained.`,
898
+ location: 'execution-logs',
899
+ evidence: `PII type "${piiType}" found in saved execution data`,
900
+ remediation: 'Configure data retention policies or mask PII before storing execution results.',
901
+ });
902
+ break; // Only report once for retention
903
+ }
904
+ }
905
+ }
906
+ }
907
+ }
908
+ catch (error) {
909
+ // Log but don't fail the entire compliance check
910
+ console.error('Runtime PII tracing failed:', error);
911
+ }
912
+ return findings;
913
+ }
914
+ /**
915
+ * Trace PII test values through execution data
916
+ */
917
+ tracePIIFlow(execution, piiTestData) {
918
+ const trace = {};
919
+ const runData = execution.data?.resultData?.runData;
920
+ if (!runData)
921
+ return trace;
922
+ for (const [nodeName, nodeRuns] of Object.entries(runData)) {
923
+ const piiFound = [];
924
+ for (const run of nodeRuns) {
925
+ const nodeDataStr = JSON.stringify(run.data || {});
926
+ for (const [piiType, piiValue] of Object.entries(piiTestData)) {
927
+ if (typeof piiValue === 'string' && nodeDataStr.includes(piiValue)) {
928
+ piiFound.push(piiType);
929
+ }
930
+ }
931
+ }
932
+ if (piiFound.length > 0) {
933
+ trace[nodeName] = [...new Set(piiFound)];
934
+ }
935
+ }
936
+ return trace;
937
+ }
938
+ /**
939
+ * Check if node is authorized to process PII
940
+ */
941
+ isAuthorizedPIINode(nodeName, execution) {
942
+ // First few nodes in execution are typically authorized (input/trigger)
943
+ const runData = execution.data?.resultData?.runData;
944
+ if (!runData)
945
+ return true;
946
+ const nodeNames = Object.keys(runData);
947
+ const nodeIndex = nodeNames.indexOf(nodeName);
948
+ // First 2 nodes are typically authorized (trigger + first processor)
949
+ return nodeIndex < 2;
950
+ }
951
+ /**
952
+ * Check if node sends data to external services
953
+ */
954
+ isExternalServiceNode(nodeName, execution) {
955
+ const runData = execution.data?.resultData?.runData;
956
+ if (!runData)
957
+ return false;
958
+ const nodeRuns = runData[nodeName];
959
+ if (!nodeRuns || nodeRuns.length === 0)
960
+ return false;
961
+ // Check source info for node type
962
+ const nodeSource = nodeRuns[0].source?.[0];
963
+ if (!nodeSource)
964
+ return false;
965
+ // HTTP, webhook, and API nodes are external
966
+ const externalPatterns = ['http', 'api', 'webhook', 'slack', 'email', 'sendgrid'];
967
+ const nodeType = nodeSource.previousNode || '';
968
+ return externalPatterns.some(p => nodeType.toLowerCase().includes(p) || nodeName.toLowerCase().includes(p));
969
+ }
970
+ /**
971
+ * Wait for workflow execution to complete
972
+ */
973
+ async waitForExecution(executionId, timeoutMs) {
974
+ const startTime = Date.now();
975
+ while (Date.now() - startTime < timeoutMs) {
976
+ const execution = await this.getExecution(executionId);
977
+ if (execution.status !== 'running' && execution.status !== 'waiting') {
978
+ return execution;
979
+ }
980
+ await new Promise(resolve => setTimeout(resolve, 500));
981
+ }
982
+ throw new Error(`Execution ${executionId} timed out after ${timeoutMs}ms`);
983
+ }
984
+ }
985
+ exports.N8nComplianceValidatorAgent = N8nComplianceValidatorAgent;
986
+ //# sourceMappingURL=N8nComplianceValidatorAgent.js.map