kailash 0.3.0__py3-none-any.whl → 0.3.2__py3-none-any.whl

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 (113) hide show
  1. kailash/access_control.py +40 -39
  2. kailash/api/auth.py +26 -32
  3. kailash/api/custom_nodes.py +29 -29
  4. kailash/api/custom_nodes_secure.py +35 -35
  5. kailash/api/database.py +17 -17
  6. kailash/api/gateway.py +19 -19
  7. kailash/api/mcp_integration.py +24 -23
  8. kailash/api/studio.py +45 -45
  9. kailash/api/workflow_api.py +8 -8
  10. kailash/cli/commands.py +5 -8
  11. kailash/manifest.py +42 -42
  12. kailash/mcp/__init__.py +1 -1
  13. kailash/mcp/ai_registry_server.py +20 -20
  14. kailash/mcp/client.py +9 -11
  15. kailash/mcp/client_new.py +10 -10
  16. kailash/mcp/server.py +1 -2
  17. kailash/mcp/server_enhanced.py +449 -0
  18. kailash/mcp/servers/ai_registry.py +6 -6
  19. kailash/mcp/utils/__init__.py +31 -0
  20. kailash/mcp/utils/cache.py +267 -0
  21. kailash/mcp/utils/config.py +263 -0
  22. kailash/mcp/utils/formatters.py +293 -0
  23. kailash/mcp/utils/metrics.py +418 -0
  24. kailash/nodes/ai/agents.py +9 -9
  25. kailash/nodes/ai/ai_providers.py +33 -34
  26. kailash/nodes/ai/embedding_generator.py +31 -32
  27. kailash/nodes/ai/intelligent_agent_orchestrator.py +62 -66
  28. kailash/nodes/ai/iterative_llm_agent.py +48 -48
  29. kailash/nodes/ai/llm_agent.py +32 -33
  30. kailash/nodes/ai/models.py +13 -13
  31. kailash/nodes/ai/self_organizing.py +44 -44
  32. kailash/nodes/api/auth.py +11 -11
  33. kailash/nodes/api/graphql.py +13 -13
  34. kailash/nodes/api/http.py +19 -19
  35. kailash/nodes/api/monitoring.py +20 -20
  36. kailash/nodes/api/rate_limiting.py +9 -13
  37. kailash/nodes/api/rest.py +29 -29
  38. kailash/nodes/api/security.py +44 -47
  39. kailash/nodes/base.py +21 -23
  40. kailash/nodes/base_async.py +7 -7
  41. kailash/nodes/base_cycle_aware.py +12 -12
  42. kailash/nodes/base_with_acl.py +5 -5
  43. kailash/nodes/code/python.py +66 -57
  44. kailash/nodes/data/directory.py +6 -6
  45. kailash/nodes/data/event_generation.py +10 -10
  46. kailash/nodes/data/file_discovery.py +28 -31
  47. kailash/nodes/data/readers.py +8 -8
  48. kailash/nodes/data/retrieval.py +10 -10
  49. kailash/nodes/data/sharepoint_graph.py +17 -17
  50. kailash/nodes/data/sources.py +5 -5
  51. kailash/nodes/data/sql.py +13 -13
  52. kailash/nodes/data/streaming.py +25 -25
  53. kailash/nodes/data/vector_db.py +22 -22
  54. kailash/nodes/data/writers.py +7 -7
  55. kailash/nodes/logic/async_operations.py +17 -17
  56. kailash/nodes/logic/convergence.py +11 -11
  57. kailash/nodes/logic/loop.py +4 -4
  58. kailash/nodes/logic/operations.py +11 -11
  59. kailash/nodes/logic/workflow.py +8 -9
  60. kailash/nodes/mixins/mcp.py +17 -17
  61. kailash/nodes/mixins.py +8 -10
  62. kailash/nodes/transform/chunkers.py +3 -3
  63. kailash/nodes/transform/formatters.py +7 -7
  64. kailash/nodes/transform/processors.py +10 -10
  65. kailash/runtime/access_controlled.py +18 -18
  66. kailash/runtime/async_local.py +17 -19
  67. kailash/runtime/docker.py +20 -22
  68. kailash/runtime/local.py +16 -16
  69. kailash/runtime/parallel.py +23 -23
  70. kailash/runtime/parallel_cyclic.py +27 -27
  71. kailash/runtime/runner.py +6 -6
  72. kailash/runtime/testing.py +20 -20
  73. kailash/sdk_exceptions.py +0 -58
  74. kailash/security.py +14 -26
  75. kailash/tracking/manager.py +38 -38
  76. kailash/tracking/metrics_collector.py +15 -14
  77. kailash/tracking/models.py +53 -53
  78. kailash/tracking/storage/base.py +7 -17
  79. kailash/tracking/storage/database.py +22 -23
  80. kailash/tracking/storage/filesystem.py +38 -40
  81. kailash/utils/export.py +21 -21
  82. kailash/utils/templates.py +2 -3
  83. kailash/visualization/api.py +30 -34
  84. kailash/visualization/dashboard.py +17 -17
  85. kailash/visualization/performance.py +16 -16
  86. kailash/visualization/reports.py +25 -27
  87. kailash/workflow/builder.py +8 -8
  88. kailash/workflow/convergence.py +13 -12
  89. kailash/workflow/cycle_analyzer.py +30 -32
  90. kailash/workflow/cycle_builder.py +12 -12
  91. kailash/workflow/cycle_config.py +16 -15
  92. kailash/workflow/cycle_debugger.py +40 -40
  93. kailash/workflow/cycle_exceptions.py +29 -29
  94. kailash/workflow/cycle_profiler.py +21 -21
  95. kailash/workflow/cycle_state.py +20 -22
  96. kailash/workflow/cyclic_runner.py +44 -44
  97. kailash/workflow/graph.py +40 -40
  98. kailash/workflow/mermaid_visualizer.py +9 -11
  99. kailash/workflow/migration.py +22 -22
  100. kailash/workflow/mock_registry.py +6 -6
  101. kailash/workflow/runner.py +9 -9
  102. kailash/workflow/safety.py +12 -13
  103. kailash/workflow/state.py +8 -11
  104. kailash/workflow/templates.py +19 -19
  105. kailash/workflow/validation.py +14 -14
  106. kailash/workflow/visualization.py +22 -22
  107. {kailash-0.3.0.dist-info → kailash-0.3.2.dist-info}/METADATA +53 -5
  108. kailash-0.3.2.dist-info/RECORD +136 -0
  109. kailash-0.3.0.dist-info/RECORD +0 -130
  110. {kailash-0.3.0.dist-info → kailash-0.3.2.dist-info}/WHEEL +0 -0
  111. {kailash-0.3.0.dist-info → kailash-0.3.2.dist-info}/entry_points.txt +0 -0
  112. {kailash-0.3.0.dist-info → kailash-0.3.2.dist-info}/licenses/LICENSE +0 -0
  113. {kailash-0.3.0.dist-info → kailash-0.3.2.dist-info}/top_level.txt +0 -0
kailash/nodes/api/rest.py CHANGED
@@ -10,7 +10,7 @@ Key Components:
10
10
  * Resource path builders and response handlers
11
11
  """
12
12
 
13
- from typing import Any, Dict, List, Optional
13
+ from typing import Any
14
14
 
15
15
  from kailash.nodes.api.http import AsyncHTTPRequestNode, HTTPRequestNode
16
16
  from kailash.nodes.base import Node, NodeParameter, register_node
@@ -157,7 +157,7 @@ class RESTClientNode(Node):
157
157
  super().__init__(**kwargs)
158
158
  self.http_node = HTTPRequestNode(url="")
159
159
 
160
- def get_parameters(self) -> Dict[str, NodeParameter]:
160
+ def get_parameters(self) -> dict[str, NodeParameter]:
161
161
  """Define the parameters this node accepts.
162
162
 
163
163
  Returns:
@@ -297,7 +297,7 @@ class RESTClientNode(Node):
297
297
  ),
298
298
  }
299
299
 
300
- def get_output_schema(self) -> Dict[str, NodeParameter]:
300
+ def get_output_schema(self) -> dict[str, NodeParameter]:
301
301
  """Define the output schema for this node.
302
302
 
303
303
  Returns:
@@ -334,8 +334,8 @@ class RESTClientNode(Node):
334
334
  self,
335
335
  base_url: str,
336
336
  resource: str,
337
- path_params: Dict[str, Any],
338
- version: Optional[str] = None,
337
+ path_params: dict[str, Any],
338
+ version: str | None = None,
339
339
  ) -> str:
340
340
  """Build the full URL for a REST API request.
341
341
 
@@ -394,10 +394,10 @@ class RESTClientNode(Node):
394
394
 
395
395
  def _handle_pagination(
396
396
  self,
397
- initial_response: Dict[str, Any],
398
- query_params: Dict[str, Any],
399
- pagination_params: Dict[str, Any],
400
- ) -> List[Any]:
397
+ initial_response: dict[str, Any],
398
+ query_params: dict[str, Any],
399
+ pagination_params: dict[str, Any],
400
+ ) -> list[Any]:
401
401
  """Handle pagination for REST API responses.
402
402
 
403
403
  This method supports common pagination patterns:
@@ -469,7 +469,7 @@ class RESTClientNode(Node):
469
469
  return all_items
470
470
 
471
471
  def _get_nested_value(
472
- self, obj: Dict[str, Any], path: str, default: Any = None
472
+ self, obj: dict[str, Any], path: str, default: Any = None
473
473
  ) -> Any:
474
474
  """Get a nested value from a dictionary using a dot-separated path.
475
475
 
@@ -495,7 +495,7 @@ class RESTClientNode(Node):
495
495
 
496
496
  return current
497
497
 
498
- def run(self, **kwargs) -> Dict[str, Any]:
498
+ def run(self, **kwargs) -> dict[str, Any]:
499
499
  """Execute a REST API request.
500
500
 
501
501
  Args:
@@ -671,8 +671,8 @@ class RESTClientNode(Node):
671
671
 
672
672
  # Convenience methods for CRUD operations
673
673
  def get(
674
- self, base_url: str, resource: str, resource_id: Optional[str] = None, **kwargs
675
- ) -> Dict[str, Any]:
674
+ self, base_url: str, resource: str, resource_id: str | None = None, **kwargs
675
+ ) -> dict[str, Any]:
676
676
  """GET a resource or list of resources.
677
677
 
678
678
  Args:
@@ -703,8 +703,8 @@ class RESTClientNode(Node):
703
703
  )
704
704
 
705
705
  def create(
706
- self, base_url: str, resource: str, data: Dict[str, Any], **kwargs
707
- ) -> Dict[str, Any]:
706
+ self, base_url: str, resource: str, data: dict[str, Any], **kwargs
707
+ ) -> dict[str, Any]:
708
708
  """CREATE (POST) a new resource.
709
709
 
710
710
  Args:
@@ -725,10 +725,10 @@ class RESTClientNode(Node):
725
725
  base_url: str,
726
726
  resource: str,
727
727
  resource_id: str,
728
- data: Dict[str, Any],
728
+ data: dict[str, Any],
729
729
  partial: bool = False,
730
730
  **kwargs,
731
- ) -> Dict[str, Any]:
731
+ ) -> dict[str, Any]:
732
732
  """UPDATE (PUT/PATCH) an existing resource.
733
733
 
734
734
  Args:
@@ -756,7 +756,7 @@ class RESTClientNode(Node):
756
756
 
757
757
  def delete(
758
758
  self, base_url: str, resource: str, resource_id: str, **kwargs
759
- ) -> Dict[str, Any]:
759
+ ) -> dict[str, Any]:
760
760
  """DELETE a resource.
761
761
 
762
762
  Args:
@@ -779,7 +779,7 @@ class RESTClientNode(Node):
779
779
  **kwargs,
780
780
  )
781
781
 
782
- def _extract_metadata(self, response: Dict[str, Any]) -> Dict[str, Any]:
782
+ def _extract_metadata(self, response: dict[str, Any]) -> dict[str, Any]:
783
783
  """Extract additional metadata from response.
784
784
 
785
785
  Args:
@@ -811,8 +811,8 @@ class RESTClientNode(Node):
811
811
  return metadata
812
812
 
813
813
  def _extract_rate_limit_metadata(
814
- self, headers: Dict[str, str]
815
- ) -> Optional[Dict[str, Any]]:
814
+ self, headers: dict[str, str]
815
+ ) -> dict[str, Any] | None:
816
816
  """Extract rate limiting information from response headers.
817
817
 
818
818
  Args:
@@ -847,8 +847,8 @@ class RESTClientNode(Node):
847
847
  return rate_limit if rate_limit else None
848
848
 
849
849
  def _extract_pagination_metadata(
850
- self, headers: Dict[str, str], content: Any
851
- ) -> Optional[Dict[str, Any]]:
850
+ self, headers: dict[str, str], content: Any
851
+ ) -> dict[str, Any] | None:
852
852
  """Extract pagination information from headers and response body.
853
853
 
854
854
  Args:
@@ -892,7 +892,7 @@ class RESTClientNode(Node):
892
892
 
893
893
  return pagination if pagination else None
894
894
 
895
- def _parse_link_header(self, link_header: str) -> Dict[str, str]:
895
+ def _parse_link_header(self, link_header: str) -> dict[str, str]:
896
896
  """Parse Link header for pagination URLs.
897
897
 
898
898
  Args:
@@ -916,7 +916,7 @@ class RESTClientNode(Node):
916
916
 
917
917
  return links
918
918
 
919
- def _extract_links(self, content: Any) -> Optional[Dict[str, Any]]:
919
+ def _extract_links(self, content: Any) -> dict[str, Any] | None:
920
920
  """Extract HATEOAS links from response content.
921
921
 
922
922
  Args:
@@ -986,7 +986,7 @@ class AsyncRESTClientNode(AsyncNode):
986
986
  self.http_node = AsyncHTTPRequestNode(**kwargs)
987
987
  self.rest_node = RESTClientNode(**kwargs)
988
988
 
989
- def get_parameters(self) -> Dict[str, NodeParameter]:
989
+ def get_parameters(self) -> dict[str, NodeParameter]:
990
990
  """Define the parameters this node accepts.
991
991
 
992
992
  Returns:
@@ -995,7 +995,7 @@ class AsyncRESTClientNode(AsyncNode):
995
995
  # Same parameters as the synchronous version
996
996
  return self.rest_node.get_parameters()
997
997
 
998
- def get_output_schema(self) -> Dict[str, NodeParameter]:
998
+ def get_output_schema(self) -> dict[str, NodeParameter]:
999
999
  """Define the output schema for this node.
1000
1000
 
1001
1001
  Returns:
@@ -1004,7 +1004,7 @@ class AsyncRESTClientNode(AsyncNode):
1004
1004
  # Same output schema as the synchronous version
1005
1005
  return self.rest_node.get_output_schema()
1006
1006
 
1007
- def run(self, **kwargs) -> Dict[str, Any]:
1007
+ def run(self, **kwargs) -> dict[str, Any]:
1008
1008
  """Synchronous version of the REST request, for compatibility.
1009
1009
 
1010
1010
  This is implemented for compatibility but users should use the
@@ -1022,7 +1022,7 @@ class AsyncRESTClientNode(AsyncNode):
1022
1022
  # Forward to the synchronous REST node
1023
1023
  return self.rest_node.run(**kwargs)
1024
1024
 
1025
- async def async_run(self, **kwargs) -> Dict[str, Any]:
1025
+ async def async_run(self, **kwargs) -> dict[str, Any]:
1026
1026
  """Execute a REST API request asynchronously.
1027
1027
 
1028
1028
  Args:
@@ -2,8 +2,8 @@
2
2
 
3
3
  import socket
4
4
  import time
5
- from datetime import datetime, timezone
6
- from typing import Any, Dict, List
5
+ from datetime import UTC, datetime
6
+ from typing import Any
7
7
  from urllib.parse import urlparse
8
8
 
9
9
  import requests
@@ -87,7 +87,7 @@ class SecurityScannerNode(Node):
87
87
  >>> assert 'security_findings' in result
88
88
  """
89
89
 
90
- def get_parameters(self) -> Dict[str, NodeParameter]:
90
+ def get_parameters(self) -> dict[str, NodeParameter]:
91
91
  return {
92
92
  "scan_types": NodeParameter(
93
93
  name="scan_types",
@@ -138,7 +138,7 @@ class SecurityScannerNode(Node):
138
138
  ),
139
139
  }
140
140
 
141
- def run(self, **kwargs) -> Dict[str, Any]:
141
+ def run(self, **kwargs) -> dict[str, Any]:
142
142
  scan_types = kwargs["scan_types"]
143
143
  targets = kwargs["targets"]
144
144
  scan_depth = kwargs.get("scan_depth", "basic")
@@ -169,7 +169,7 @@ class SecurityScannerNode(Node):
169
169
  "severity": "info",
170
170
  "title": f"Scan Error: {scan_type}",
171
171
  "description": f"Failed to complete {scan_type} scan: {str(e)}",
172
- "timestamp": datetime.now(timezone.utc).isoformat() + "Z",
172
+ "timestamp": datetime.now(UTC).isoformat() + "Z",
173
173
  }
174
174
  target_findings.append(error_finding)
175
175
 
@@ -205,12 +205,12 @@ class SecurityScannerNode(Node):
205
205
  [f for f in all_findings if 1 <= f.get("risk_score", 0) < 4]
206
206
  ),
207
207
  "execution_time": execution_time,
208
- "timestamp": datetime.now(timezone.utc).isoformat() + "Z",
208
+ "timestamp": datetime.now(UTC).isoformat() + "Z",
209
209
  }
210
210
 
211
211
  def _perform_scan(
212
212
  self, scan_type: str, target: str, scan_depth: str, ports: str, timeout: int
213
- ) -> List[Dict[str, Any]]:
213
+ ) -> list[dict[str, Any]]:
214
214
  """Perform a specific type of security scan."""
215
215
 
216
216
  if scan_type == "web_security":
@@ -233,13 +233,13 @@ class SecurityScannerNode(Node):
233
233
  "severity": "info",
234
234
  "title": f"Unsupported Scan Type: {scan_type}",
235
235
  "description": f"Scan type '{scan_type}' is not supported",
236
- "timestamp": datetime.now(timezone.utc).isoformat() + "Z",
236
+ "timestamp": datetime.now(UTC).isoformat() + "Z",
237
237
  }
238
238
  ]
239
239
 
240
240
  def _scan_web_security(
241
241
  self, target: str, scan_depth: str, timeout: int
242
- ) -> List[Dict[str, Any]]:
242
+ ) -> list[dict[str, Any]]:
243
243
  """Perform web application security scan."""
244
244
  findings = []
245
245
 
@@ -266,13 +266,13 @@ class SecurityScannerNode(Node):
266
266
  "severity": "medium",
267
267
  "title": "Connection Error",
268
268
  "description": f"Failed to connect to target: {str(e)}",
269
- "timestamp": datetime.now(timezone.utc).isoformat() + "Z",
269
+ "timestamp": datetime.now(UTC).isoformat() + "Z",
270
270
  }
271
271
  )
272
272
 
273
273
  return findings
274
274
 
275
- def _scan_ssl(self, target: str, timeout: int) -> List[Dict[str, Any]]:
275
+ def _scan_ssl(self, target: str, timeout: int) -> list[dict[str, Any]]:
276
276
  """Perform SSL/TLS security scan."""
277
277
  findings = []
278
278
 
@@ -290,7 +290,7 @@ class SecurityScannerNode(Node):
290
290
  "title": "SSL/TLS Not Used",
291
291
  "description": "Target does not use HTTPS encryption",
292
292
  "recommendation": "Implement SSL/TLS encryption",
293
- "timestamp": datetime.now(timezone.utc).isoformat() + "Z",
293
+ "timestamp": datetime.now(UTC).isoformat() + "Z",
294
294
  }
295
295
  )
296
296
  return findings
@@ -323,8 +323,7 @@ class SecurityScannerNode(Node):
323
323
  "title": "SSL Certificate Expiring",
324
324
  "description": f"SSL certificate expires in {days_until_expiry} days",
325
325
  "details": {"expiry_date": cert["notAfter"]},
326
- "timestamp": datetime.now(timezone.utc).isoformat()
327
- + "Z",
326
+ "timestamp": datetime.now(UTC).isoformat() + "Z",
328
327
  }
329
328
  )
330
329
 
@@ -336,7 +335,7 @@ class SecurityScannerNode(Node):
336
335
  "severity": "low",
337
336
  "title": "SSL Check Error",
338
337
  "description": f"Failed to perform detailed SSL check: {str(e)}",
339
- "timestamp": datetime.now(timezone.utc).isoformat() + "Z",
338
+ "timestamp": datetime.now(UTC).isoformat() + "Z",
340
339
  }
341
340
  )
342
341
 
@@ -348,7 +347,7 @@ class SecurityScannerNode(Node):
348
347
  "severity": "low",
349
348
  "title": "SSL Scan Error",
350
349
  "description": f"SSL scan failed: {str(e)}",
351
- "timestamp": datetime.now(timezone.utc).isoformat() + "Z",
350
+ "timestamp": datetime.now(UTC).isoformat() + "Z",
352
351
  }
353
352
  )
354
353
 
@@ -356,7 +355,7 @@ class SecurityScannerNode(Node):
356
355
 
357
356
  def _scan_ports(
358
357
  self, target: str, ports: str, timeout: int
359
- ) -> List[Dict[str, Any]]:
358
+ ) -> list[dict[str, Any]]:
360
359
  """Perform port scan."""
361
360
  findings = []
362
361
 
@@ -432,8 +431,7 @@ class SecurityScannerNode(Node):
432
431
  "description": f"Port {port} ({service}) is open and accessible",
433
432
  "details": {"port": port, "service": service},
434
433
  "recommendation": f"Ensure {service} service is properly secured",
435
- "timestamp": datetime.now(timezone.utc).isoformat()
436
- + "Z",
434
+ "timestamp": datetime.now(UTC).isoformat() + "Z",
437
435
  }
438
436
  )
439
437
 
@@ -453,7 +451,7 @@ class SecurityScannerNode(Node):
453
451
  "open_ports": open_ports,
454
452
  "total_scanned": len(port_list),
455
453
  },
456
- "timestamp": datetime.now(timezone.utc).isoformat() + "Z",
454
+ "timestamp": datetime.now(UTC).isoformat() + "Z",
457
455
  }
458
456
  )
459
457
 
@@ -461,7 +459,7 @@ class SecurityScannerNode(Node):
461
459
 
462
460
  def _scan_services(
463
461
  self, target: str, ports: str, timeout: int
464
- ) -> List[Dict[str, Any]]:
462
+ ) -> list[dict[str, Any]]:
465
463
  """Perform service detection scan."""
466
464
  # For now, this is a simplified version
467
465
  # In a full implementation, you would use nmap or similar tools
@@ -469,7 +467,7 @@ class SecurityScannerNode(Node):
469
467
 
470
468
  def _scan_vulnerabilities(
471
469
  self, target: str, scan_depth: str, timeout: int
472
- ) -> List[Dict[str, Any]]:
470
+ ) -> list[dict[str, Any]]:
473
471
  """Perform vulnerability scan using known CVE patterns."""
474
472
  findings = []
475
473
 
@@ -500,7 +498,7 @@ class SecurityScannerNode(Node):
500
498
  "potential_cve": vuln_info["cve"],
501
499
  },
502
500
  "recommendation": "Update server software to latest version",
503
- "timestamp": datetime.now(timezone.utc).isoformat() + "Z",
501
+ "timestamp": datetime.now(UTC).isoformat() + "Z",
504
502
  }
505
503
  )
506
504
 
@@ -512,13 +510,13 @@ class SecurityScannerNode(Node):
512
510
  "severity": "low",
513
511
  "title": "Vulnerability Scan Error",
514
512
  "description": f"Failed to perform vulnerability scan: {str(e)}",
515
- "timestamp": datetime.now(timezone.utc).isoformat() + "Z",
513
+ "timestamp": datetime.now(UTC).isoformat() + "Z",
516
514
  }
517
515
  )
518
516
 
519
517
  return findings
520
518
 
521
- def _scan_headers(self, target: str, timeout: int) -> List[Dict[str, Any]]:
519
+ def _scan_headers(self, target: str, timeout: int) -> list[dict[str, Any]]:
522
520
  """Perform HTTP security headers analysis."""
523
521
  findings = []
524
522
 
@@ -546,7 +544,7 @@ class SecurityScannerNode(Node):
546
544
  "title": f"Missing Security Header: {header}",
547
545
  "description": f"Missing {header} header for {description}",
548
546
  "recommendation": f"Implement {header} header",
549
- "timestamp": datetime.now(timezone.utc).isoformat() + "Z",
547
+ "timestamp": datetime.now(UTC).isoformat() + "Z",
550
548
  }
551
549
  )
552
550
 
@@ -558,7 +556,7 @@ class SecurityScannerNode(Node):
558
556
  "severity": "low",
559
557
  "title": "Header Scan Error",
560
558
  "description": f"Failed to analyze headers: {str(e)}",
561
- "timestamp": datetime.now(timezone.utc).isoformat() + "Z",
559
+ "timestamp": datetime.now(UTC).isoformat() + "Z",
562
560
  }
563
561
  )
564
562
 
@@ -566,13 +564,13 @@ class SecurityScannerNode(Node):
566
564
 
567
565
  def _check_security_headers(
568
566
  self, target: str, response: requests.Response
569
- ) -> List[Dict[str, Any]]:
567
+ ) -> list[dict[str, Any]]:
570
568
  """Check for security headers in response."""
571
569
  return self._scan_headers(target, 30) # Reuse header scan logic
572
570
 
573
571
  def _check_ssl_redirect(
574
572
  self, target: str, response: requests.Response
575
- ) -> List[Dict[str, Any]]:
573
+ ) -> list[dict[str, Any]]:
576
574
  """Check if HTTP redirects to HTTPS."""
577
575
  findings = []
578
576
 
@@ -585,7 +583,7 @@ class SecurityScannerNode(Node):
585
583
  "title": "No HTTPS Redirect",
586
584
  "description": "HTTP requests are not redirected to HTTPS",
587
585
  "recommendation": "Implement automatic HTTPS redirect",
588
- "timestamp": datetime.now(timezone.utc).isoformat() + "Z",
586
+ "timestamp": datetime.now(UTC).isoformat() + "Z",
589
587
  }
590
588
  )
591
589
 
@@ -593,7 +591,7 @@ class SecurityScannerNode(Node):
593
591
 
594
592
  def _check_directory_listing(
595
593
  self, target: str, response: requests.Response
596
- ) -> List[Dict[str, Any]]:
594
+ ) -> list[dict[str, Any]]:
597
595
  """Check for directory listing vulnerabilities."""
598
596
  findings = []
599
597
 
@@ -606,13 +604,13 @@ class SecurityScannerNode(Node):
606
604
  "title": "Directory Listing Enabled",
607
605
  "description": "Directory listing is enabled, potentially exposing sensitive files",
608
606
  "recommendation": "Disable directory listing",
609
- "timestamp": datetime.now(timezone.utc).isoformat() + "Z",
607
+ "timestamp": datetime.now(UTC).isoformat() + "Z",
610
608
  }
611
609
  )
612
610
 
613
611
  return findings
614
612
 
615
- def _check_common_files(self, target: str, timeout: int) -> List[Dict[str, Any]]:
613
+ def _check_common_files(self, target: str, timeout: int) -> list[dict[str, Any]]:
616
614
  """Check for common sensitive files."""
617
615
  findings = []
618
616
 
@@ -646,7 +644,7 @@ class SecurityScannerNode(Node):
646
644
  "description": f"Sensitive file {file_path} is publicly accessible",
647
645
  "details": {"file_path": file_path, "url": url},
648
646
  "recommendation": "Restrict access to sensitive files",
649
- "timestamp": datetime.now(timezone.utc).isoformat() + "Z",
647
+ "timestamp": datetime.now(UTC).isoformat() + "Z",
650
648
  }
651
649
  )
652
650
  except:
@@ -656,7 +654,7 @@ class SecurityScannerNode(Node):
656
654
 
657
655
  def _check_injection_points(
658
656
  self, target: str, timeout: int
659
- ) -> List[Dict[str, Any]]:
657
+ ) -> list[dict[str, Any]]:
660
658
  """Check for basic injection vulnerabilities."""
661
659
  # This is a simplified check - real implementations would be much more thorough
662
660
  findings = []
@@ -683,7 +681,7 @@ class SecurityScannerNode(Node):
683
681
  "title": "Potential Injection Vulnerability",
684
682
  "description": f"Test payload reflected in response: {payload[:50]}...",
685
683
  "recommendation": "Implement proper input validation and sanitization",
686
- "timestamp": datetime.now(timezone.utc).isoformat() + "Z",
684
+ "timestamp": datetime.now(UTC).isoformat() + "Z",
687
685
  }
688
686
  )
689
687
  break # Don't continue testing if one is found
@@ -692,7 +690,7 @@ class SecurityScannerNode(Node):
692
690
 
693
691
  return findings
694
692
 
695
- def _check_authentication(self, target: str, timeout: int) -> List[Dict[str, Any]]:
693
+ def _check_authentication(self, target: str, timeout: int) -> list[dict[str, Any]]:
696
694
  """Check for authentication-related issues."""
697
695
  findings = []
698
696
 
@@ -716,8 +714,7 @@ class SecurityScannerNode(Node):
716
714
  "description": f"Login page at {path} is not using HTTPS",
717
715
  "details": {"login_path": path},
718
716
  "recommendation": "Use HTTPS for all authentication pages",
719
- "timestamp": datetime.now(timezone.utc).isoformat()
720
- + "Z",
717
+ "timestamp": datetime.now(UTC).isoformat() + "Z",
721
718
  }
722
719
  )
723
720
  except:
@@ -726,8 +723,8 @@ class SecurityScannerNode(Node):
726
723
  return findings
727
724
 
728
725
  def _calculate_risk_scores(
729
- self, findings: List[Dict[str, Any]]
730
- ) -> List[Dict[str, Any]]:
726
+ self, findings: list[dict[str, Any]]
727
+ ) -> list[dict[str, Any]]:
731
728
  """Calculate risk scores for findings."""
732
729
  severity_scores = {
733
730
  "critical": 10,
@@ -757,8 +754,8 @@ class SecurityScannerNode(Node):
757
754
  return findings
758
755
 
759
756
  def _add_compliance_mapping(
760
- self, findings: List[Dict[str, Any]]
761
- ) -> List[Dict[str, Any]]:
757
+ self, findings: list[dict[str, Any]]
758
+ ) -> list[dict[str, Any]]:
762
759
  """Add compliance framework mapping to findings."""
763
760
  compliance_mapping = {
764
761
  "missing_security_header": ["OWASP Top 10", "PCI DSS"],
@@ -777,11 +774,11 @@ class SecurityScannerNode(Node):
777
774
 
778
775
  def _generate_scan_summary(
779
776
  self,
780
- findings: List[Dict],
781
- targets: List[str],
782
- scan_types: List[str],
777
+ findings: list[dict],
778
+ targets: list[str],
779
+ scan_types: list[str],
783
780
  execution_time: float,
784
- ) -> Dict[str, Any]:
781
+ ) -> dict[str, Any]:
785
782
  """Generate summary of security scan results."""
786
783
 
787
784
  # Count findings by severity