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.
- {scythe_ttp-0.10.0/scythe_ttp.egg-info → scythe_ttp-0.12.1}/PKG-INFO +103 -14
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/README.md +103 -14
- scythe_ttp-0.12.1/VERSION +1 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/core/executor.py +38 -5
- scythe_ttp-0.12.1/scythe/core/headers.py +194 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/journeys/base.py +21 -2
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/journeys/executor.py +22 -1
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1/scythe_ttp.egg-info}/PKG-INFO +103 -14
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe_ttp.egg-info/SOURCES.txt +2 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/setup.py +1 -1
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/tests/test_feature_completeness.py +4 -1
- scythe_ttp-0.12.1/tests/test_header_extraction.py +400 -0
- scythe_ttp-0.10.0/VERSION +0 -1
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/LICENSE +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/MANIFEST.in +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/requirements.txt +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/__init__.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/auth/__init__.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/auth/base.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/auth/basic.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/auth/bearer.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/behaviors/__init__.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/behaviors/base.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/behaviors/default.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/behaviors/human.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/behaviors/machine.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/behaviors/stealth.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/core/__init__.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/core/ttp.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/journeys/__init__.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/journeys/actions.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/orchestrators/__init__.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/orchestrators/base.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/orchestrators/batch.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/orchestrators/distributed.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/orchestrators/scale.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/payloads/__init__.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/payloads/generators.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/ttps/__init__.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/ttps/web/__init__.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/ttps/web/login_bruteforce.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/ttps/web/sql_injection.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe/ttps/web/uuid_guessing.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe_ttp.egg-info/dependency_links.txt +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe_ttp.egg-info/requires.txt +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/scythe_ttp.egg-info/top_level.txt +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/setup.cfg +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/tests/test_authentication.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/tests/test_behaviors.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/tests/test_expected_results.py +0 -0
- {scythe_ttp-0.10.0 → scythe_ttp-0.12.1}/tests/test_journeys.py +0 -0
- {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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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).")
|