scythe-ttp 0.11.0__tar.gz → 0.12.1__tar.gz

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.

Potentially problematic release.


This version of scythe-ttp might be problematic. Click here for more details.

Files changed (52) hide show
  1. {scythe_ttp-0.11.0/scythe_ttp.egg-info → scythe_ttp-0.12.1}/PKG-INFO +36 -16
  2. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/README.md +36 -16
  3. scythe_ttp-0.12.1/VERSION +1 -0
  4. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/core/headers.py +45 -45
  5. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1/scythe_ttp.egg-info}/PKG-INFO +36 -16
  6. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/setup.py +1 -1
  7. scythe_ttp-0.11.0/VERSION +0 -1
  8. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/LICENSE +0 -0
  9. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/MANIFEST.in +0 -0
  10. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/requirements.txt +0 -0
  11. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/__init__.py +0 -0
  12. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/auth/__init__.py +0 -0
  13. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/auth/base.py +0 -0
  14. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/auth/basic.py +0 -0
  15. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/auth/bearer.py +0 -0
  16. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/behaviors/__init__.py +0 -0
  17. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/behaviors/base.py +0 -0
  18. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/behaviors/default.py +0 -0
  19. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/behaviors/human.py +0 -0
  20. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/behaviors/machine.py +0 -0
  21. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/behaviors/stealth.py +0 -0
  22. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/core/__init__.py +0 -0
  23. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/core/executor.py +0 -0
  24. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/core/ttp.py +0 -0
  25. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/journeys/__init__.py +0 -0
  26. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/journeys/actions.py +0 -0
  27. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/journeys/base.py +0 -0
  28. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/journeys/executor.py +0 -0
  29. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/orchestrators/__init__.py +0 -0
  30. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/orchestrators/base.py +0 -0
  31. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/orchestrators/batch.py +0 -0
  32. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/orchestrators/distributed.py +0 -0
  33. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/orchestrators/scale.py +0 -0
  34. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/payloads/__init__.py +0 -0
  35. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/payloads/generators.py +0 -0
  36. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/ttps/__init__.py +0 -0
  37. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/ttps/web/__init__.py +0 -0
  38. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/ttps/web/login_bruteforce.py +0 -0
  39. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/ttps/web/sql_injection.py +0 -0
  40. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe/ttps/web/uuid_guessing.py +0 -0
  41. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe_ttp.egg-info/SOURCES.txt +0 -0
  42. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe_ttp.egg-info/dependency_links.txt +0 -0
  43. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe_ttp.egg-info/requires.txt +0 -0
  44. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/scythe_ttp.egg-info/top_level.txt +0 -0
  45. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/setup.cfg +0 -0
  46. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/tests/test_authentication.py +0 -0
  47. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/tests/test_behaviors.py +0 -0
  48. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/tests/test_expected_results.py +0 -0
  49. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/tests/test_feature_completeness.py +0 -0
  50. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/tests/test_header_extraction.py +0 -0
  51. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/tests/test_journeys.py +0 -0
  52. {scythe_ttp-0.11.0 → scythe_ttp-0.12.1}/tests/test_orchestrators.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scythe-ttp
3
- Version: 0.11.0
3
+ Version: 0.12.1
4
4
  Summary: An extensible framework for emulating attacker TTPs with Selenium.
5
5
  Home-page: https://github.com/EpykLab/scythe
6
6
  Author: EpykLab
@@ -169,6 +169,26 @@ file_upload_ttp = FileUploadTTP(
169
169
 
170
170
  ### Installation
171
171
 
172
+ #### If you would like to use as a library:
173
+
174
+ setup the virtual environment
175
+ ```bash
176
+ python3 -m venv venv
177
+
178
+ # source the venv
179
+ # bash,zsh: source venv/bin/activate
180
+ # fish: source venv/bin/activate.fish
181
+ ```
182
+
183
+ install the package
184
+ ```bash
185
+ # in an activated venv
186
+
187
+ pip3 install scythe-ttp
188
+ ```
189
+
190
+ #### If you would like like to contribute:
191
+
172
192
  1. Clone the repository:
173
193
  ```bash
174
194
  git clone https://github.com/EpykLab/scythe.git
@@ -458,11 +478,11 @@ from scythe.core.executor import TTPExecutor
458
478
  class MyTTP(TTP):
459
479
  def get_payloads(self):
460
480
  yield "test_payload"
461
-
481
+
462
482
  def execute_step(self, driver, payload):
463
483
  driver.get("http://your-app.com/login")
464
484
  # ... test logic ...
465
-
485
+
466
486
  def verify_result(self, driver):
467
487
  return "welcome" in driver.page_source
468
488
 
@@ -523,7 +543,7 @@ from typing import Generator, Any
523
543
 
524
544
  class CustomBusinessLogicTTP(TTP):
525
545
  """Test specific business logic under adverse conditions."""
526
-
546
+
527
547
  def __init__(self, business_scenarios: list, expected_result: bool = True):
528
548
  super().__init__(
529
549
  name="Business Logic Test",
@@ -531,28 +551,28 @@ class CustomBusinessLogicTTP(TTP):
531
551
  expected_result=expected_result
532
552
  )
533
553
  self.scenarios = business_scenarios
534
-
554
+
535
555
  def get_payloads(self) -> Generator[Any, None, None]:
536
556
  for scenario in self.scenarios:
537
557
  yield scenario
538
-
558
+
539
559
  def execute_step(self, driver, payload):
540
560
  # Implement your specific business logic testing
541
561
  # This could involve API calls, database interactions, etc.
542
562
  pass
543
-
563
+
544
564
  def verify_result(self, driver) -> bool:
545
565
  # Verify the business logic behaved correctly
546
566
  return self.check_business_rules(driver)
547
567
 
548
568
  class CustomWorkflowAction(Action):
549
569
  """Custom action for specific workflow steps."""
550
-
570
+
551
571
  def __init__(self, workflow_step: str, parameters: dict):
552
572
  super().__init__(f"Custom {workflow_step}", f"Execute {workflow_step}")
553
573
  self.workflow_step = workflow_step
554
574
  self.parameters = parameters
555
-
575
+
556
576
  def execute(self, driver, context):
557
577
  # Implement custom workflow logic
558
578
  return self.perform_workflow_step(driver, context)
@@ -569,12 +589,12 @@ ecommerce_suite = [
569
589
  payment_security_test, # Test payment form security
570
590
  user_data_protection_test, # Test PII protection
571
591
  session_management_test, # Test session security
572
-
592
+
573
593
  # Load testing
574
594
  product_catalog_load_test, # High-traffic product browsing
575
595
  checkout_process_load_test, # Concurrent checkout processes
576
596
  search_functionality_test, # Search under load
577
-
597
+
578
598
  # Workflow testing
579
599
  complete_purchase_journey, # End-to-end purchase flow
580
600
  return_process_journey, # Product return workflow
@@ -647,30 +667,30 @@ def analyze_test_results(orchestration_result):
647
667
  print("="*60)
648
668
  print("COMPREHENSIVE TEST ANALYSIS")
649
669
  print("="*60)
650
-
670
+
651
671
  print(f"Total Executions: {orchestration_result.total_executions}")
652
672
  print(f"Success Rate: {orchestration_result.success_rate:.1f}%")
653
673
  print(f"Average Execution Time: {orchestration_result.average_execution_time:.2f}s")
654
-
674
+
655
675
  # Performance metrics
656
676
  if orchestration_result.metadata.get('performance_stats'):
657
677
  stats = orchestration_result.metadata['performance_stats']
658
678
  print(f"Peak Response Time: {stats.get('peak_response_time', 'N/A')}")
659
679
  print(f"95th Percentile: {stats.get('p95_response_time', 'N/A')}")
660
-
680
+
661
681
  # Geographic distribution (if applicable)
662
682
  if orchestration_result.metadata.get('distribution_stats'):
663
683
  dist = orchestration_result.metadata['distribution_stats']
664
684
  print("Geographic Distribution:")
665
685
  for location, count in dist.get('location_usage', {}).items():
666
686
  print(f" {location}: {count} executions")
667
-
687
+
668
688
  # Error analysis
669
689
  if orchestration_result.errors:
670
690
  print(f"\nErrors Encountered: {len(orchestration_result.errors)}")
671
691
  for i, error in enumerate(orchestration_result.errors[:5], 1):
672
692
  print(f" {i}. {error}")
673
-
693
+
674
694
  print("="*60)
675
695
 
676
696
  # Use with any orchestration result
@@ -122,6 +122,26 @@ file_upload_ttp = FileUploadTTP(
122
122
 
123
123
  ### Installation
124
124
 
125
+ #### If you would like to use as a library:
126
+
127
+ setup the virtual environment
128
+ ```bash
129
+ python3 -m venv venv
130
+
131
+ # source the venv
132
+ # bash,zsh: source venv/bin/activate
133
+ # fish: source venv/bin/activate.fish
134
+ ```
135
+
136
+ install the package
137
+ ```bash
138
+ # in an activated venv
139
+
140
+ pip3 install scythe-ttp
141
+ ```
142
+
143
+ #### If you would like like to contribute:
144
+
125
145
  1. Clone the repository:
126
146
  ```bash
127
147
  git clone https://github.com/EpykLab/scythe.git
@@ -411,11 +431,11 @@ from scythe.core.executor import TTPExecutor
411
431
  class MyTTP(TTP):
412
432
  def get_payloads(self):
413
433
  yield "test_payload"
414
-
434
+
415
435
  def execute_step(self, driver, payload):
416
436
  driver.get("http://your-app.com/login")
417
437
  # ... test logic ...
418
-
438
+
419
439
  def verify_result(self, driver):
420
440
  return "welcome" in driver.page_source
421
441
 
@@ -476,7 +496,7 @@ from typing import Generator, Any
476
496
 
477
497
  class CustomBusinessLogicTTP(TTP):
478
498
  """Test specific business logic under adverse conditions."""
479
-
499
+
480
500
  def __init__(self, business_scenarios: list, expected_result: bool = True):
481
501
  super().__init__(
482
502
  name="Business Logic Test",
@@ -484,28 +504,28 @@ class CustomBusinessLogicTTP(TTP):
484
504
  expected_result=expected_result
485
505
  )
486
506
  self.scenarios = business_scenarios
487
-
507
+
488
508
  def get_payloads(self) -> Generator[Any, None, None]:
489
509
  for scenario in self.scenarios:
490
510
  yield scenario
491
-
511
+
492
512
  def execute_step(self, driver, payload):
493
513
  # Implement your specific business logic testing
494
514
  # This could involve API calls, database interactions, etc.
495
515
  pass
496
-
516
+
497
517
  def verify_result(self, driver) -> bool:
498
518
  # Verify the business logic behaved correctly
499
519
  return self.check_business_rules(driver)
500
520
 
501
521
  class CustomWorkflowAction(Action):
502
522
  """Custom action for specific workflow steps."""
503
-
523
+
504
524
  def __init__(self, workflow_step: str, parameters: dict):
505
525
  super().__init__(f"Custom {workflow_step}", f"Execute {workflow_step}")
506
526
  self.workflow_step = workflow_step
507
527
  self.parameters = parameters
508
-
528
+
509
529
  def execute(self, driver, context):
510
530
  # Implement custom workflow logic
511
531
  return self.perform_workflow_step(driver, context)
@@ -522,12 +542,12 @@ ecommerce_suite = [
522
542
  payment_security_test, # Test payment form security
523
543
  user_data_protection_test, # Test PII protection
524
544
  session_management_test, # Test session security
525
-
545
+
526
546
  # Load testing
527
547
  product_catalog_load_test, # High-traffic product browsing
528
548
  checkout_process_load_test, # Concurrent checkout processes
529
549
  search_functionality_test, # Search under load
530
-
550
+
531
551
  # Workflow testing
532
552
  complete_purchase_journey, # End-to-end purchase flow
533
553
  return_process_journey, # Product return workflow
@@ -600,30 +620,30 @@ def analyze_test_results(orchestration_result):
600
620
  print("="*60)
601
621
  print("COMPREHENSIVE TEST ANALYSIS")
602
622
  print("="*60)
603
-
623
+
604
624
  print(f"Total Executions: {orchestration_result.total_executions}")
605
625
  print(f"Success Rate: {orchestration_result.success_rate:.1f}%")
606
626
  print(f"Average Execution Time: {orchestration_result.average_execution_time:.2f}s")
607
-
627
+
608
628
  # Performance metrics
609
629
  if orchestration_result.metadata.get('performance_stats'):
610
630
  stats = orchestration_result.metadata['performance_stats']
611
631
  print(f"Peak Response Time: {stats.get('peak_response_time', 'N/A')}")
612
632
  print(f"95th Percentile: {stats.get('p95_response_time', 'N/A')}")
613
-
633
+
614
634
  # Geographic distribution (if applicable)
615
635
  if orchestration_result.metadata.get('distribution_stats'):
616
636
  dist = orchestration_result.metadata['distribution_stats']
617
637
  print("Geographic Distribution:")
618
638
  for location, count in dist.get('location_usage', {}).items():
619
639
  print(f" {location}: {count} executions")
620
-
640
+
621
641
  # Error analysis
622
642
  if orchestration_result.errors:
623
643
  print(f"\nErrors Encountered: {len(orchestration_result.errors)}")
624
644
  for i, error in enumerate(orchestration_result.errors[:5], 1):
625
645
  print(f" {i}. {error}")
626
-
646
+
627
647
  print("="*60)
628
648
 
629
649
  # Use with any orchestration result
@@ -724,4 +744,4 @@ This architecture supports testing scenarios from simple security checks to comp
724
744
 
725
745
  ---
726
746
 
727
- **Scythe**: Comprehensive adverse conditions testing for robust, reliable systems.
747
+ **Scythe**: Comprehensive adverse conditions testing for robust, reliable systems.
@@ -0,0 +1 @@
1
+ 0.12.2
@@ -8,23 +8,23 @@ from selenium.webdriver.chrome.options import Options
8
8
  class HeaderExtractor:
9
9
  """
10
10
  Utility class for extracting HTTP response headers from WebDriver sessions.
11
-
11
+
12
12
  Specifically designed to capture the X-SCYTHE-TARGET-VERSION header
13
13
  that indicates the version of the web application being tested.
14
14
  """
15
-
16
- SCYTHE_VERSION_HEADER = "X-SCYTHE-TARGET-VERSION"
17
-
15
+
16
+ SCYTHE_VERSION_HEADER = "X-Scythe-Target-Version"
17
+
18
18
  def __init__(self):
19
19
  self.logger = logging.getLogger("HeaderExtractor")
20
-
20
+
21
21
  @staticmethod
22
22
  def enable_logging_for_driver(chrome_options: Options) -> None:
23
23
  """
24
24
  Enable performance logging capabilities for Chrome WebDriver.
25
-
25
+
26
26
  This must be called during WebDriver setup to capture network logs.
27
-
27
+
28
28
  Args:
29
29
  chrome_options: Chrome options object to modify
30
30
  """
@@ -32,15 +32,15 @@ class HeaderExtractor:
32
32
  chrome_options.add_argument("--enable-logging")
33
33
  chrome_options.add_argument("--log-level=0")
34
34
  chrome_options.set_capability("goog:loggingPrefs", {"performance": "ALL"})
35
-
35
+
36
36
  def extract_target_version(self, driver: WebDriver, target_url: Optional[str] = None) -> Optional[str]:
37
37
  """
38
38
  Extract the X-SCYTHE-TARGET-VERSION header from the most recent HTTP response.
39
-
39
+
40
40
  Args:
41
41
  driver: WebDriver instance with performance logging enabled
42
42
  target_url: Optional URL to filter responses for (if None, uses any response)
43
-
43
+
44
44
  Returns:
45
45
  Version string if header found, None otherwise
46
46
  """
@@ -49,77 +49,77 @@ class HeaderExtractor:
49
49
  if not hasattr(driver, 'get_log'):
50
50
  self.logger.warning("WebDriver does not support get_log method")
51
51
  return None
52
-
52
+
53
53
  logs = getattr(driver, 'get_log')('performance')
54
-
54
+
55
55
  # Look for Network.responseReceived events
56
56
  for log_entry in reversed(logs): # Start with most recent
57
57
  try:
58
58
  message = log_entry.get('message', {})
59
59
  if isinstance(message, str):
60
60
  message = json.loads(message)
61
-
61
+
62
62
  method = message.get('message', {}).get('method', '')
63
63
  params = message.get('message', {}).get('params', {})
64
-
64
+
65
65
  if method == 'Network.responseReceived':
66
66
  response = params.get('response', {})
67
67
  headers = response.get('headers', {})
68
68
  response_url = response.get('url', '')
69
-
69
+
70
70
  # Filter by target URL if specified
71
71
  if target_url and target_url not in response_url:
72
72
  continue
73
-
73
+
74
74
  # Look for the version header (case-insensitive)
75
75
  version = self._find_version_header(headers)
76
76
  if version:
77
77
  self.logger.debug(f"Found target version '{version}' in response from {response_url}")
78
78
  return version
79
-
79
+
80
80
  except (json.JSONDecodeError, KeyError, AttributeError) as e:
81
81
  self.logger.debug(f"Error parsing log entry: {e}")
82
82
  continue
83
-
83
+
84
84
  self.logger.debug("No X-SCYTHE-TARGET-VERSION header found in network logs")
85
85
  return None
86
-
86
+
87
87
  except Exception as e:
88
88
  self.logger.warning(f"Failed to extract target version from logs: {e}")
89
89
  return None
90
-
90
+
91
91
  def _find_version_header(self, headers: Dict[str, Any]) -> Optional[str]:
92
92
  """
93
93
  Find the version header in a case-insensitive manner.
94
-
94
+
95
95
  Args:
96
96
  headers: Dictionary of HTTP headers
97
-
97
+
98
98
  Returns:
99
99
  Version string if found, None otherwise
100
100
  """
101
101
  # Check for exact case match first
102
102
  if self.SCYTHE_VERSION_HEADER in headers:
103
103
  return str(headers[self.SCYTHE_VERSION_HEADER]).strip()
104
-
104
+
105
105
  # Check case-insensitive
106
106
  header_lower = self.SCYTHE_VERSION_HEADER.lower()
107
107
  for header_name, header_value in headers.items():
108
108
  if header_name.lower() == header_lower:
109
109
  return str(header_value).strip()
110
-
110
+
111
111
  return None
112
-
112
+
113
113
  def extract_all_headers(self, driver: WebDriver, target_url: Optional[str] = None) -> Dict[str, str]:
114
114
  """
115
115
  Extract all headers from the most recent HTTP response.
116
-
116
+
117
117
  Useful for debugging or when additional headers might be needed.
118
-
118
+
119
119
  Args:
120
120
  driver: WebDriver instance with performance logging enabled
121
121
  target_url: Optional URL to filter responses for
122
-
122
+
123
123
  Returns:
124
124
  Dictionary of headers from the most recent response
125
125
  """
@@ -128,67 +128,67 @@ class HeaderExtractor:
128
128
  if not hasattr(driver, 'get_log'):
129
129
  self.logger.warning("WebDriver does not support get_log method")
130
130
  return {}
131
-
131
+
132
132
  logs = getattr(driver, 'get_log')('performance')
133
-
133
+
134
134
  for log_entry in reversed(logs):
135
135
  try:
136
136
  message = log_entry.get('message', {})
137
137
  if isinstance(message, str):
138
138
  message = json.loads(message)
139
-
139
+
140
140
  method = message.get('message', {}).get('method', '')
141
141
  params = message.get('message', {}).get('params', {})
142
-
142
+
143
143
  if method == 'Network.responseReceived':
144
144
  response = params.get('response', {})
145
145
  headers = response.get('headers', {})
146
146
  response_url = response.get('url', '')
147
-
147
+
148
148
  # Filter by target URL if specified
149
149
  if target_url and target_url not in response_url:
150
150
  continue
151
-
151
+
152
152
  # Convert all header values to strings
153
153
  return {k: str(v) for k, v in headers.items()}
154
-
154
+
155
155
  except (json.JSONDecodeError, KeyError, AttributeError):
156
156
  continue
157
-
157
+
158
158
  return {}
159
-
159
+
160
160
  except Exception as e:
161
161
  self.logger.warning(f"Failed to extract headers from logs: {e}")
162
162
  return {}
163
-
163
+
164
164
  def get_version_summary(self, results: list) -> Dict[str, Any]:
165
165
  """
166
166
  Analyze version information across multiple test results.
167
-
167
+
168
168
  Args:
169
169
  results: List of result dictionaries containing version information
170
-
170
+
171
171
  Returns:
172
172
  Dictionary with version analysis summary
173
173
  """
174
174
  versions = []
175
175
  results_with_version = 0
176
-
176
+
177
177
  for result in results:
178
178
  version = result.get('target_version')
179
179
  if version:
180
180
  versions.append(version)
181
181
  results_with_version += 1
182
-
182
+
183
183
  summary = {
184
184
  'total_results': len(results),
185
185
  'results_with_version': results_with_version,
186
186
  'unique_versions': list(set(versions)) if versions else [],
187
187
  'version_counts': {}
188
188
  }
189
-
189
+
190
190
  # Count occurrences of each version
191
191
  for version in versions:
192
192
  summary['version_counts'][version] = summary['version_counts'].get(version, 0) + 1
193
-
194
- return summary
193
+
194
+ return summary
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scythe-ttp
3
- Version: 0.11.0
3
+ Version: 0.12.1
4
4
  Summary: An extensible framework for emulating attacker TTPs with Selenium.
5
5
  Home-page: https://github.com/EpykLab/scythe
6
6
  Author: EpykLab
@@ -169,6 +169,26 @@ file_upload_ttp = FileUploadTTP(
169
169
 
170
170
  ### Installation
171
171
 
172
+ #### If you would like to use as a library:
173
+
174
+ setup the virtual environment
175
+ ```bash
176
+ python3 -m venv venv
177
+
178
+ # source the venv
179
+ # bash,zsh: source venv/bin/activate
180
+ # fish: source venv/bin/activate.fish
181
+ ```
182
+
183
+ install the package
184
+ ```bash
185
+ # in an activated venv
186
+
187
+ pip3 install scythe-ttp
188
+ ```
189
+
190
+ #### If you would like like to contribute:
191
+
172
192
  1. Clone the repository:
173
193
  ```bash
174
194
  git clone https://github.com/EpykLab/scythe.git
@@ -458,11 +478,11 @@ from scythe.core.executor import TTPExecutor
458
478
  class MyTTP(TTP):
459
479
  def get_payloads(self):
460
480
  yield "test_payload"
461
-
481
+
462
482
  def execute_step(self, driver, payload):
463
483
  driver.get("http://your-app.com/login")
464
484
  # ... test logic ...
465
-
485
+
466
486
  def verify_result(self, driver):
467
487
  return "welcome" in driver.page_source
468
488
 
@@ -523,7 +543,7 @@ from typing import Generator, Any
523
543
 
524
544
  class CustomBusinessLogicTTP(TTP):
525
545
  """Test specific business logic under adverse conditions."""
526
-
546
+
527
547
  def __init__(self, business_scenarios: list, expected_result: bool = True):
528
548
  super().__init__(
529
549
  name="Business Logic Test",
@@ -531,28 +551,28 @@ class CustomBusinessLogicTTP(TTP):
531
551
  expected_result=expected_result
532
552
  )
533
553
  self.scenarios = business_scenarios
534
-
554
+
535
555
  def get_payloads(self) -> Generator[Any, None, None]:
536
556
  for scenario in self.scenarios:
537
557
  yield scenario
538
-
558
+
539
559
  def execute_step(self, driver, payload):
540
560
  # Implement your specific business logic testing
541
561
  # This could involve API calls, database interactions, etc.
542
562
  pass
543
-
563
+
544
564
  def verify_result(self, driver) -> bool:
545
565
  # Verify the business logic behaved correctly
546
566
  return self.check_business_rules(driver)
547
567
 
548
568
  class CustomWorkflowAction(Action):
549
569
  """Custom action for specific workflow steps."""
550
-
570
+
551
571
  def __init__(self, workflow_step: str, parameters: dict):
552
572
  super().__init__(f"Custom {workflow_step}", f"Execute {workflow_step}")
553
573
  self.workflow_step = workflow_step
554
574
  self.parameters = parameters
555
-
575
+
556
576
  def execute(self, driver, context):
557
577
  # Implement custom workflow logic
558
578
  return self.perform_workflow_step(driver, context)
@@ -569,12 +589,12 @@ ecommerce_suite = [
569
589
  payment_security_test, # Test payment form security
570
590
  user_data_protection_test, # Test PII protection
571
591
  session_management_test, # Test session security
572
-
592
+
573
593
  # Load testing
574
594
  product_catalog_load_test, # High-traffic product browsing
575
595
  checkout_process_load_test, # Concurrent checkout processes
576
596
  search_functionality_test, # Search under load
577
-
597
+
578
598
  # Workflow testing
579
599
  complete_purchase_journey, # End-to-end purchase flow
580
600
  return_process_journey, # Product return workflow
@@ -647,30 +667,30 @@ def analyze_test_results(orchestration_result):
647
667
  print("="*60)
648
668
  print("COMPREHENSIVE TEST ANALYSIS")
649
669
  print("="*60)
650
-
670
+
651
671
  print(f"Total Executions: {orchestration_result.total_executions}")
652
672
  print(f"Success Rate: {orchestration_result.success_rate:.1f}%")
653
673
  print(f"Average Execution Time: {orchestration_result.average_execution_time:.2f}s")
654
-
674
+
655
675
  # Performance metrics
656
676
  if orchestration_result.metadata.get('performance_stats'):
657
677
  stats = orchestration_result.metadata['performance_stats']
658
678
  print(f"Peak Response Time: {stats.get('peak_response_time', 'N/A')}")
659
679
  print(f"95th Percentile: {stats.get('p95_response_time', 'N/A')}")
660
-
680
+
661
681
  # Geographic distribution (if applicable)
662
682
  if orchestration_result.metadata.get('distribution_stats'):
663
683
  dist = orchestration_result.metadata['distribution_stats']
664
684
  print("Geographic Distribution:")
665
685
  for location, count in dist.get('location_usage', {}).items():
666
686
  print(f" {location}: {count} executions")
667
-
687
+
668
688
  # Error analysis
669
689
  if orchestration_result.errors:
670
690
  print(f"\nErrors Encountered: {len(orchestration_result.errors)}")
671
691
  for i, error in enumerate(orchestration_result.errors[:5], 1):
672
692
  print(f" {i}. {error}")
673
-
693
+
674
694
  print("="*60)
675
695
 
676
696
  # Use with any orchestration result
@@ -8,7 +8,7 @@ with open("./requirements.txt", "r", encoding="utf-8") as f:
8
8
 
9
9
  setuptools.setup(
10
10
  name="scythe-ttp",
11
- version="0.11.0",
11
+ version="0.12.1",
12
12
  author="EpykLab",
13
13
  author_email="cyber@epyklab.com",
14
14
  description="An extensible framework for emulating attacker TTPs with Selenium.",
scythe_ttp-0.11.0/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.12.1
File without changes
File without changes
File without changes