scythe-ttp 0.10.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.10.0/scythe_ttp.egg-info → scythe_ttp-0.12.1}/PKG-INFO +103 -14
  2. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/README.md +103 -14
  3. scythe_ttp-0.12.1/VERSION +1 -0
  4. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/core/executor.py +38 -5
  5. scythe_ttp-0.12.1/scythe/core/headers.py +194 -0
  6. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/journeys/base.py +21 -2
  7. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/journeys/executor.py +22 -1
  8. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1/scythe_ttp.egg-info}/PKG-INFO +103 -14
  9. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe_ttp.egg-info/SOURCES.txt +2 -0
  10. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/setup.py +1 -1
  11. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/tests/test_feature_completeness.py +4 -1
  12. scythe_ttp-0.12.1/tests/test_header_extraction.py +400 -0
  13. scythe_ttp-0.10.0/VERSION +0 -1
  14. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/LICENSE +0 -0
  15. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/MANIFEST.in +0 -0
  16. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/requirements.txt +0 -0
  17. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/__init__.py +0 -0
  18. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/auth/__init__.py +0 -0
  19. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/auth/base.py +0 -0
  20. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/auth/basic.py +0 -0
  21. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/auth/bearer.py +0 -0
  22. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/behaviors/__init__.py +0 -0
  23. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/behaviors/base.py +0 -0
  24. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/behaviors/default.py +0 -0
  25. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/behaviors/human.py +0 -0
  26. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/behaviors/machine.py +0 -0
  27. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/behaviors/stealth.py +0 -0
  28. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/core/__init__.py +0 -0
  29. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/core/ttp.py +0 -0
  30. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/journeys/__init__.py +0 -0
  31. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/journeys/actions.py +0 -0
  32. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/orchestrators/__init__.py +0 -0
  33. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/orchestrators/base.py +0 -0
  34. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/orchestrators/batch.py +0 -0
  35. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/orchestrators/distributed.py +0 -0
  36. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/orchestrators/scale.py +0 -0
  37. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/payloads/__init__.py +0 -0
  38. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/payloads/generators.py +0 -0
  39. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/ttps/__init__.py +0 -0
  40. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/ttps/web/__init__.py +0 -0
  41. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/ttps/web/login_bruteforce.py +0 -0
  42. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/ttps/web/sql_injection.py +0 -0
  43. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/ttps/web/uuid_guessing.py +0 -0
  44. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe_ttp.egg-info/dependency_links.txt +0 -0
  45. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe_ttp.egg-info/requires.txt +0 -0
  46. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe_ttp.egg-info/top_level.txt +0 -0
  47. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/setup.cfg +0 -0
  48. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/tests/test_authentication.py +0 -0
  49. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/tests/test_behaviors.py +0 -0
  50. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/tests/test_expected_results.py +0 -0
  51. {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/tests/test_journeys.py +0 -0
  52. {scythe_ttp-0.10.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.10.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
@@ -96,6 +96,7 @@ Scythe operates on the principle that robust systems must be tested under advers
96
96
  ### 📊 **Professional Reporting**
97
97
  * **Clear Result Indicators**: ✓ Expected outcomes, ✗ Unexpected results
98
98
  * **Comprehensive Logging**: Detailed execution tracking and analysis
99
+ * **Version Detection**: Automatic extraction of X-SCYTHE-TARGET-VERSION headers
99
100
  * **Performance Metrics**: Timing, success rates, and resource utilization
100
101
  * **Execution Statistics**: Detailed reporting across all test types
101
102
 
@@ -168,6 +169,26 @@ file_upload_ttp = FileUploadTTP(
168
169
 
169
170
  ### Installation
170
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
+
171
192
  1. Clone the repository:
172
193
  ```bash
173
194
  git clone https://github.com/EpykLab/scythe.git
@@ -443,6 +464,74 @@ executor = TTPExecutor(
443
464
  )
444
465
  ```
445
466
 
467
+ ### Version Detection
468
+
469
+ Scythe automatically captures the `X-SCYTHE-TARGET-VERSION` header from HTTP responses to track which version of your web application is being tested:
470
+
471
+ ```python
472
+ from scythe.core.ttp import TTP
473
+ from scythe.core.executor import TTPExecutor
474
+
475
+ # Your web application should set this header:
476
+ # X-SCYTHE-TARGET-VERSION: 1.3.2
477
+
478
+ class MyTTP(TTP):
479
+ def get_payloads(self):
480
+ yield "test_payload"
481
+
482
+ def execute_step(self, driver, payload):
483
+ driver.get("http://your-app.com/login")
484
+ # ... test logic ...
485
+
486
+ def verify_result(self, driver):
487
+ return "welcome" in driver.page_source
488
+
489
+ # Run the test
490
+ ttp = MyTTP("Version Test", "Test with version detection")
491
+ executor = TTPExecutor(ttp=ttp, target_url="http://your-app.com")
492
+ executor.run()
493
+ ```
494
+
495
+ **Output includes version information:**
496
+ ```
497
+ ✓ EXPECTED SUCCESS: 'test_payload' | Version: 1.3.2
498
+ Target Version Summary:
499
+ Results with version info: 1/1
500
+ Version 1.3.2: 1 result(s)
501
+ ```
502
+
503
+ **Server-side implementation examples:**
504
+ ```python
505
+ # Python/Flask
506
+ @app.after_request
507
+ def add_version_header(response):
508
+ response.headers['X-SCYTHE-TARGET-VERSION'] = '1.3.2'
509
+ return response
510
+
511
+ # Node.js/Express
512
+ app.use((req, res, next) => {
513
+ res.set('X-SCYTHE-TARGET-VERSION', '1.3.2');
514
+ next();
515
+ });
516
+
517
+ # Java/Spring Boot
518
+ @Component
519
+ public class VersionHeaderFilter implements Filter {
520
+ @Override
521
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
522
+ HttpServletResponse httpResponse = (HttpServletResponse) response;
523
+ httpResponse.setHeader("X-SCYTHE-TARGET-VERSION", "1.3.2");
524
+ chain.doFilter(request, response);
525
+ }
526
+ }
527
+ ```
528
+
529
+ This feature helps you:
530
+ - **Track test results** by application version
531
+ - **Verify deployment status** during testing
532
+ - **Correlate issues** with specific software versions
533
+ - **Ensure consistency** across test environments
534
+
446
535
  ### Custom Test Creation
447
536
 
448
537
  Extend Scythe for specific testing needs:
@@ -454,7 +543,7 @@ from typing import Generator, Any
454
543
 
455
544
  class CustomBusinessLogicTTP(TTP):
456
545
  """Test specific business logic under adverse conditions."""
457
-
546
+
458
547
  def __init__(self, business_scenarios: list, expected_result: bool = True):
459
548
  super().__init__(
460
549
  name="Business Logic Test",
@@ -462,28 +551,28 @@ class CustomBusinessLogicTTP(TTP):
462
551
  expected_result=expected_result
463
552
  )
464
553
  self.scenarios = business_scenarios
465
-
554
+
466
555
  def get_payloads(self) -> Generator[Any, None, None]:
467
556
  for scenario in self.scenarios:
468
557
  yield scenario
469
-
558
+
470
559
  def execute_step(self, driver, payload):
471
560
  # Implement your specific business logic testing
472
561
  # This could involve API calls, database interactions, etc.
473
562
  pass
474
-
563
+
475
564
  def verify_result(self, driver) -> bool:
476
565
  # Verify the business logic behaved correctly
477
566
  return self.check_business_rules(driver)
478
567
 
479
568
  class CustomWorkflowAction(Action):
480
569
  """Custom action for specific workflow steps."""
481
-
570
+
482
571
  def __init__(self, workflow_step: str, parameters: dict):
483
572
  super().__init__(f"Custom {workflow_step}", f"Execute {workflow_step}")
484
573
  self.workflow_step = workflow_step
485
574
  self.parameters = parameters
486
-
575
+
487
576
  def execute(self, driver, context):
488
577
  # Implement custom workflow logic
489
578
  return self.perform_workflow_step(driver, context)
@@ -500,12 +589,12 @@ ecommerce_suite = [
500
589
  payment_security_test, # Test payment form security
501
590
  user_data_protection_test, # Test PII protection
502
591
  session_management_test, # Test session security
503
-
592
+
504
593
  # Load testing
505
594
  product_catalog_load_test, # High-traffic product browsing
506
595
  checkout_process_load_test, # Concurrent checkout processes
507
596
  search_functionality_test, # Search under load
508
-
597
+
509
598
  # Workflow testing
510
599
  complete_purchase_journey, # End-to-end purchase flow
511
600
  return_process_journey, # Product return workflow
@@ -578,30 +667,30 @@ def analyze_test_results(orchestration_result):
578
667
  print("="*60)
579
668
  print("COMPREHENSIVE TEST ANALYSIS")
580
669
  print("="*60)
581
-
670
+
582
671
  print(f"Total Executions: {orchestration_result.total_executions}")
583
672
  print(f"Success Rate: {orchestration_result.success_rate:.1f}%")
584
673
  print(f"Average Execution Time: {orchestration_result.average_execution_time:.2f}s")
585
-
674
+
586
675
  # Performance metrics
587
676
  if orchestration_result.metadata.get('performance_stats'):
588
677
  stats = orchestration_result.metadata['performance_stats']
589
678
  print(f"Peak Response Time: {stats.get('peak_response_time', 'N/A')}")
590
679
  print(f"95th Percentile: {stats.get('p95_response_time', 'N/A')}")
591
-
680
+
592
681
  # Geographic distribution (if applicable)
593
682
  if orchestration_result.metadata.get('distribution_stats'):
594
683
  dist = orchestration_result.metadata['distribution_stats']
595
684
  print("Geographic Distribution:")
596
685
  for location, count in dist.get('location_usage', {}).items():
597
686
  print(f" {location}: {count} executions")
598
-
687
+
599
688
  # Error analysis
600
689
  if orchestration_result.errors:
601
690
  print(f"\nErrors Encountered: {len(orchestration_result.errors)}")
602
691
  for i, error in enumerate(orchestration_result.errors[:5], 1):
603
692
  print(f" {i}. {error}")
604
-
693
+
605
694
  print("="*60)
606
695
 
607
696
  # Use with any orchestration result
@@ -49,6 +49,7 @@ Scythe operates on the principle that robust systems must be tested under advers
49
49
  ### 📊 **Professional Reporting**
50
50
  * **Clear Result Indicators**: ✓ Expected outcomes, ✗ Unexpected results
51
51
  * **Comprehensive Logging**: Detailed execution tracking and analysis
52
+ * **Version Detection**: Automatic extraction of X-SCYTHE-TARGET-VERSION headers
52
53
  * **Performance Metrics**: Timing, success rates, and resource utilization
53
54
  * **Execution Statistics**: Detailed reporting across all test types
54
55
 
@@ -121,6 +122,26 @@ file_upload_ttp = FileUploadTTP(
121
122
 
122
123
  ### Installation
123
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
+
124
145
  1. Clone the repository:
125
146
  ```bash
126
147
  git clone https://github.com/EpykLab/scythe.git
@@ -396,6 +417,74 @@ executor = TTPExecutor(
396
417
  )
397
418
  ```
398
419
 
420
+ ### Version Detection
421
+
422
+ Scythe automatically captures the `X-SCYTHE-TARGET-VERSION` header from HTTP responses to track which version of your web application is being tested:
423
+
424
+ ```python
425
+ from scythe.core.ttp import TTP
426
+ from scythe.core.executor import TTPExecutor
427
+
428
+ # Your web application should set this header:
429
+ # X-SCYTHE-TARGET-VERSION: 1.3.2
430
+
431
+ class MyTTP(TTP):
432
+ def get_payloads(self):
433
+ yield "test_payload"
434
+
435
+ def execute_step(self, driver, payload):
436
+ driver.get("http://your-app.com/login")
437
+ # ... test logic ...
438
+
439
+ def verify_result(self, driver):
440
+ return "welcome" in driver.page_source
441
+
442
+ # Run the test
443
+ ttp = MyTTP("Version Test", "Test with version detection")
444
+ executor = TTPExecutor(ttp=ttp, target_url="http://your-app.com")
445
+ executor.run()
446
+ ```
447
+
448
+ **Output includes version information:**
449
+ ```
450
+ ✓ EXPECTED SUCCESS: 'test_payload' | Version: 1.3.2
451
+ Target Version Summary:
452
+ Results with version info: 1/1
453
+ Version 1.3.2: 1 result(s)
454
+ ```
455
+
456
+ **Server-side implementation examples:**
457
+ ```python
458
+ # Python/Flask
459
+ @app.after_request
460
+ def add_version_header(response):
461
+ response.headers['X-SCYTHE-TARGET-VERSION'] = '1.3.2'
462
+ return response
463
+
464
+ # Node.js/Express
465
+ app.use((req, res, next) => {
466
+ res.set('X-SCYTHE-TARGET-VERSION', '1.3.2');
467
+ next();
468
+ });
469
+
470
+ # Java/Spring Boot
471
+ @Component
472
+ public class VersionHeaderFilter implements Filter {
473
+ @Override
474
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
475
+ HttpServletResponse httpResponse = (HttpServletResponse) response;
476
+ httpResponse.setHeader("X-SCYTHE-TARGET-VERSION", "1.3.2");
477
+ chain.doFilter(request, response);
478
+ }
479
+ }
480
+ ```
481
+
482
+ This feature helps you:
483
+ - **Track test results** by application version
484
+ - **Verify deployment status** during testing
485
+ - **Correlate issues** with specific software versions
486
+ - **Ensure consistency** across test environments
487
+
399
488
  ### Custom Test Creation
400
489
 
401
490
  Extend Scythe for specific testing needs:
@@ -407,7 +496,7 @@ from typing import Generator, Any
407
496
 
408
497
  class CustomBusinessLogicTTP(TTP):
409
498
  """Test specific business logic under adverse conditions."""
410
-
499
+
411
500
  def __init__(self, business_scenarios: list, expected_result: bool = True):
412
501
  super().__init__(
413
502
  name="Business Logic Test",
@@ -415,28 +504,28 @@ class CustomBusinessLogicTTP(TTP):
415
504
  expected_result=expected_result
416
505
  )
417
506
  self.scenarios = business_scenarios
418
-
507
+
419
508
  def get_payloads(self) -> Generator[Any, None, None]:
420
509
  for scenario in self.scenarios:
421
510
  yield scenario
422
-
511
+
423
512
  def execute_step(self, driver, payload):
424
513
  # Implement your specific business logic testing
425
514
  # This could involve API calls, database interactions, etc.
426
515
  pass
427
-
516
+
428
517
  def verify_result(self, driver) -> bool:
429
518
  # Verify the business logic behaved correctly
430
519
  return self.check_business_rules(driver)
431
520
 
432
521
  class CustomWorkflowAction(Action):
433
522
  """Custom action for specific workflow steps."""
434
-
523
+
435
524
  def __init__(self, workflow_step: str, parameters: dict):
436
525
  super().__init__(f"Custom {workflow_step}", f"Execute {workflow_step}")
437
526
  self.workflow_step = workflow_step
438
527
  self.parameters = parameters
439
-
528
+
440
529
  def execute(self, driver, context):
441
530
  # Implement custom workflow logic
442
531
  return self.perform_workflow_step(driver, context)
@@ -453,12 +542,12 @@ ecommerce_suite = [
453
542
  payment_security_test, # Test payment form security
454
543
  user_data_protection_test, # Test PII protection
455
544
  session_management_test, # Test session security
456
-
545
+
457
546
  # Load testing
458
547
  product_catalog_load_test, # High-traffic product browsing
459
548
  checkout_process_load_test, # Concurrent checkout processes
460
549
  search_functionality_test, # Search under load
461
-
550
+
462
551
  # Workflow testing
463
552
  complete_purchase_journey, # End-to-end purchase flow
464
553
  return_process_journey, # Product return workflow
@@ -531,30 +620,30 @@ def analyze_test_results(orchestration_result):
531
620
  print("="*60)
532
621
  print("COMPREHENSIVE TEST ANALYSIS")
533
622
  print("="*60)
534
-
623
+
535
624
  print(f"Total Executions: {orchestration_result.total_executions}")
536
625
  print(f"Success Rate: {orchestration_result.success_rate:.1f}%")
537
626
  print(f"Average Execution Time: {orchestration_result.average_execution_time:.2f}s")
538
-
627
+
539
628
  # Performance metrics
540
629
  if orchestration_result.metadata.get('performance_stats'):
541
630
  stats = orchestration_result.metadata['performance_stats']
542
631
  print(f"Peak Response Time: {stats.get('peak_response_time', 'N/A')}")
543
632
  print(f"95th Percentile: {stats.get('p95_response_time', 'N/A')}")
544
-
633
+
545
634
  # Geographic distribution (if applicable)
546
635
  if orchestration_result.metadata.get('distribution_stats'):
547
636
  dist = orchestration_result.metadata['distribution_stats']
548
637
  print("Geographic Distribution:")
549
638
  for location, count in dist.get('location_usage', {}).items():
550
639
  print(f" {location}: {count} executions")
551
-
640
+
552
641
  # Error analysis
553
642
  if orchestration_result.errors:
554
643
  print(f"\nErrors Encountered: {len(orchestration_result.errors)}")
555
644
  for i, error in enumerate(orchestration_result.errors[:5], 1):
556
645
  print(f" {i}. {error}")
557
-
646
+
558
647
  print("="*60)
559
648
 
560
649
  # Use with any orchestration result
@@ -655,4 +744,4 @@ This architecture supports testing scenarios from simple security checks to comp
655
744
 
656
745
  ---
657
746
 
658
- **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
@@ -5,6 +5,7 @@ from selenium.webdriver.chrome.options import Options
5
5
  from .ttp import TTP
6
6
  from typing import Optional
7
7
  from ..behaviors.base import Behavior
8
+ from .headers import HeaderExtractor
8
9
 
9
10
  # Configure logging
10
11
  logging.basicConfig(
@@ -33,8 +34,12 @@ class TTPExecutor:
33
34
  self.chrome_options.add_argument("--no-sandbox")
34
35
  self.chrome_options.add_argument("--disable-dev-shm-usage")
35
36
 
37
+ # Enable header extraction capabilities
38
+ HeaderExtractor.enable_logging_for_driver(self.chrome_options)
39
+
36
40
  self.driver = None
37
41
  self.results = []
42
+ self.header_extractor = HeaderExtractor()
38
43
 
39
44
  def _setup_driver(self):
40
45
  """Initializes the WebDriver."""
@@ -108,13 +113,27 @@ class TTPExecutor:
108
113
  if success:
109
114
  consecutive_failures = 0
110
115
  current_url = self.driver.current_url if self.driver else "unknown"
111
- result_entry = {'payload': payload, 'url': current_url, 'expected': self.ttp.expected_result, 'actual': True}
116
+
117
+ # Extract target version header
118
+ target_version = None
119
+ if self.driver:
120
+ target_version = self.header_extractor.extract_target_version(self.driver, self.target_url)
121
+
122
+ result_entry = {
123
+ 'payload': payload,
124
+ 'url': current_url,
125
+ 'expected': self.ttp.expected_result,
126
+ 'actual': True,
127
+ 'target_version': target_version
128
+ }
112
129
  self.results.append(result_entry)
113
130
 
114
131
  if self.ttp.expected_result:
115
- self.logger.info(f"EXPECTED SUCCESS: '{payload}'")
132
+ version_info = f" | Version: {target_version}" if target_version else ""
133
+ self.logger.info(f"EXPECTED SUCCESS: '{payload}'{version_info}")
116
134
  else:
117
- self.logger.warning(f"UNEXPECTED SUCCESS: '{payload}' (expected to fail)")
135
+ version_info = f" | Version: {target_version}" if target_version else ""
136
+ self.logger.warning(f"UNEXPECTED SUCCESS: '{payload}' (expected to fail){version_info}")
118
137
  else:
119
138
  consecutive_failures += 1
120
139
  if self.ttp.expected_result:
@@ -168,12 +187,26 @@ class TTPExecutor:
168
187
  if expected_successes:
169
188
  self.logger.info(f"Expected successes: {len(expected_successes)}")
170
189
  for result in expected_successes:
171
- self.logger.info(f" Payload: {result['payload']} | URL: {result['url']}")
190
+ version_info = f" | Version: {result['target_version']}" if result.get('target_version') else ""
191
+ self.logger.info(f" ✓ Payload: {result['payload']} | URL: {result['url']}{version_info}")
172
192
 
173
193
  if unexpected_successes:
174
194
  self.logger.warning(f"Unexpected successes: {len(unexpected_successes)}")
175
195
  for result in unexpected_successes:
176
- self.logger.warning(f" Payload: {result['payload']} | URL: {result['url']}")
196
+ version_info = f" | Version: {result['target_version']}" if result.get('target_version') else ""
197
+ self.logger.warning(f" ✗ Payload: {result['payload']} | URL: {result['url']}{version_info}")
198
+
199
+ # Display version summary
200
+ version_summary = self.header_extractor.get_version_summary(self.results)
201
+ if version_summary['results_with_version'] > 0:
202
+ self.logger.info("\nTarget Version Summary:")
203
+ self.logger.info(f" Results with version info: {version_summary['results_with_version']}/{version_summary['total_results']}")
204
+ if version_summary['unique_versions']:
205
+ for version in version_summary['unique_versions']:
206
+ count = version_summary['version_counts'][version]
207
+ self.logger.info(f" Version {version}: {count} result(s)")
208
+ else:
209
+ self.logger.info("\nNo X-SCYTHE-TARGET-VERSION headers detected in responses.")
177
210
  else:
178
211
  if self.ttp.expected_result:
179
212
  self.logger.info("No successes detected (expected to find vulnerabilities).")