scythe-ttp 0.17.0__tar.gz → 0.17.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.
- {scythe_ttp-0.17.0/scythe_ttp.egg-info → scythe_ttp-0.17.1}/PKG-INFO +1 -1
- scythe_ttp-0.17.1/VERSION +1 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/cli/main.py +13 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/core/executor.py +18 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/journeys/executor.py +6 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1/scythe_ttp.egg-info}/PKG-INFO +1 -1
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/tests/test_expected_results.py +57 -0
- scythe_ttp-0.17.0/VERSION +0 -1
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/LICENSE +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/MANIFEST.in +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/README.md +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/pyproject.toml +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/requirements.txt +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/__init__.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/auth/__init__.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/auth/base.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/auth/basic.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/auth/bearer.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/auth/cookie_jwt.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/behaviors/__init__.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/behaviors/base.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/behaviors/default.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/behaviors/human.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/behaviors/machine.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/behaviors/stealth.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/cli/__init__.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/core/__init__.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/core/headers.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/core/ttp.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/journeys/__init__.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/journeys/actions.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/journeys/base.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/orchestrators/__init__.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/orchestrators/base.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/orchestrators/batch.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/orchestrators/distributed.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/orchestrators/scale.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/payloads/__init__.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/payloads/generators.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/ttps/__init__.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/ttps/web/__init__.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/ttps/web/login_bruteforce.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/ttps/web/sql_injection.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe/ttps/web/uuid_guessing.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe_ttp.egg-info/SOURCES.txt +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe_ttp.egg-info/dependency_links.txt +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe_ttp.egg-info/entry_points.txt +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe_ttp.egg-info/requires.txt +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/scythe_ttp.egg-info/top_level.txt +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/setup.cfg +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/tests/test_api_models.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/tests/test_authentication.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/tests/test_behaviors.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/tests/test_cli.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/tests/test_cookie_jwt_auth.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/tests/test_feature_completeness.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/tests/test_header_extraction.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/tests/test_journeys.py +0 -0
- {scythe_ttp-0.17.0 → scythe_ttp-0.17.1}/tests/test_orchestrators.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.17.1
|
|
@@ -68,6 +68,19 @@ def check_version_in_response_header(args) -> bool:
|
|
|
68
68
|
def scythe_test_definition(args) -> bool:
|
|
69
69
|
# TODO: implement your test using Scythe primitives.
|
|
70
70
|
# Example placeholder that simply passes.
|
|
71
|
+
|
|
72
|
+
# Example usage with TTPExecutor:
|
|
73
|
+
# from scythe.core.executor import TTPExecutor
|
|
74
|
+
# executor = TTPExecutor(ttp=my_ttp, target_url=args.url)
|
|
75
|
+
# executor.run()
|
|
76
|
+
# return executor.was_successful() # Returns True if all results matched expectations
|
|
77
|
+
|
|
78
|
+
# Example usage with JourneyExecutor:
|
|
79
|
+
# from scythe.journeys.executor import JourneyExecutor
|
|
80
|
+
# executor = JourneyExecutor(journey=my_journey, target_url=args.url)
|
|
81
|
+
# executor.run()
|
|
82
|
+
# return executor.was_successful() # Returns True if journey succeeded as expected
|
|
83
|
+
|
|
71
84
|
return True
|
|
72
85
|
|
|
73
86
|
|
|
@@ -40,6 +40,7 @@ class TTPExecutor:
|
|
|
40
40
|
self.driver = None
|
|
41
41
|
self.results = []
|
|
42
42
|
self.header_extractor = HeaderExtractor()
|
|
43
|
+
self.has_test_failures = False # Track if any test had unexpected results
|
|
43
44
|
|
|
44
45
|
def _setup_driver(self):
|
|
45
46
|
"""Initializes the WebDriver."""
|
|
@@ -134,10 +135,12 @@ class TTPExecutor:
|
|
|
134
135
|
else:
|
|
135
136
|
version_info = f" | Version: {target_version}" if target_version else ""
|
|
136
137
|
self.logger.warning(f"UNEXPECTED SUCCESS: '{payload}' (expected to fail){version_info}")
|
|
138
|
+
self.has_test_failures = True # Mark as failure when result differs from expected
|
|
137
139
|
else:
|
|
138
140
|
consecutive_failures += 1
|
|
139
141
|
if self.ttp.expected_result:
|
|
140
142
|
self.logger.info(f"EXPECTED FAILURE: '{payload}' (security control working)")
|
|
143
|
+
self.has_test_failures = True # Mark as failure when result differs from expected
|
|
141
144
|
else:
|
|
142
145
|
self.logger.info(f"EXPECTED FAILURE: '{payload}'")
|
|
143
146
|
|
|
@@ -212,3 +215,18 @@ class TTPExecutor:
|
|
|
212
215
|
self.logger.info("No successes detected (expected to find vulnerabilities).")
|
|
213
216
|
else:
|
|
214
217
|
self.logger.info("No successes detected (security controls working as expected).")
|
|
218
|
+
|
|
219
|
+
# Log overall test status
|
|
220
|
+
if self.has_test_failures:
|
|
221
|
+
self.logger.error("\n✗ TEST FAILED: One or more test results differed from expected")
|
|
222
|
+
else:
|
|
223
|
+
self.logger.info("\n✓ TEST PASSED: All test results matched expectations")
|
|
224
|
+
|
|
225
|
+
def was_successful(self) -> bool:
|
|
226
|
+
"""
|
|
227
|
+
Check if all test results matched expectations.
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
True if all test results matched expectations, False otherwise
|
|
231
|
+
"""
|
|
232
|
+
return not self.has_test_failures
|
|
@@ -406,6 +406,12 @@ class JourneyExecutor:
|
|
|
406
406
|
else:
|
|
407
407
|
self.logger.info("\nNo X-SCYTHE-TARGET-VERSION headers detected in responses.")
|
|
408
408
|
|
|
409
|
+
# Log overall test status (similar to TTPExecutor)
|
|
410
|
+
if self.was_successful():
|
|
411
|
+
self.logger.info("\n✓ TEST PASSED: Journey results matched expectations")
|
|
412
|
+
else:
|
|
413
|
+
self.logger.error("\n✗ TEST FAILED: Journey results differed from expected")
|
|
414
|
+
|
|
409
415
|
self.logger.info("="*60)
|
|
410
416
|
|
|
411
417
|
def get_results(self) -> Optional[Dict[str, Any]]:
|
|
@@ -296,6 +296,63 @@ class TestExpectedResults(unittest.TestCase):
|
|
|
296
296
|
|
|
297
297
|
self.assertTrue(ttp_pass.expected_result)
|
|
298
298
|
self.assertFalse(ttp_fail.expected_result)
|
|
299
|
+
|
|
300
|
+
@patch('scythe.core.executor.webdriver.Chrome')
|
|
301
|
+
def test_was_successful_with_expected_results(self, mock_webdriver):
|
|
302
|
+
"""Test was_successful() returns True when all results match expectations."""
|
|
303
|
+
mock_webdriver.return_value = self.mock_driver
|
|
304
|
+
|
|
305
|
+
# Test with expected successes
|
|
306
|
+
ttp = MockTTP(
|
|
307
|
+
name="Test TTP",
|
|
308
|
+
description="Test description",
|
|
309
|
+
expected_result=True,
|
|
310
|
+
success_results=[True, True]
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
executor = TTPExecutor(ttp=ttp, target_url="http://test.com", headless=True)
|
|
314
|
+
executor.run()
|
|
315
|
+
|
|
316
|
+
# Should return True since results matched expectations
|
|
317
|
+
self.assertTrue(executor.was_successful())
|
|
318
|
+
|
|
319
|
+
@patch('scythe.core.executor.webdriver.Chrome')
|
|
320
|
+
def test_was_successful_with_unexpected_results(self, mock_webdriver):
|
|
321
|
+
"""Test was_successful() returns False when results don't match expectations."""
|
|
322
|
+
mock_webdriver.return_value = self.mock_driver
|
|
323
|
+
|
|
324
|
+
# Test with unexpected successes (expected to fail but succeeded)
|
|
325
|
+
ttp = MockTTP(
|
|
326
|
+
name="Test TTP",
|
|
327
|
+
description="Test description",
|
|
328
|
+
expected_result=False,
|
|
329
|
+
success_results=[True]
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
executor = TTPExecutor(ttp=ttp, target_url="http://test.com", headless=True)
|
|
333
|
+
executor.run()
|
|
334
|
+
|
|
335
|
+
# Should return False since we got unexpected success
|
|
336
|
+
self.assertFalse(executor.was_successful())
|
|
337
|
+
|
|
338
|
+
@patch('scythe.core.executor.webdriver.Chrome')
|
|
339
|
+
def test_was_successful_with_unexpected_failures(self, mock_webdriver):
|
|
340
|
+
"""Test was_successful() returns False when expected success but got failure."""
|
|
341
|
+
mock_webdriver.return_value = self.mock_driver
|
|
342
|
+
|
|
343
|
+
# Test expecting success but getting failure
|
|
344
|
+
ttp = MockTTP(
|
|
345
|
+
name="Test TTP",
|
|
346
|
+
description="Test description",
|
|
347
|
+
expected_result=True,
|
|
348
|
+
success_results=[False, False]
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
executor = TTPExecutor(ttp=ttp, target_url="http://test.com", headless=True)
|
|
352
|
+
executor.run()
|
|
353
|
+
|
|
354
|
+
# Should return False since we expected success but got failures
|
|
355
|
+
self.assertFalse(executor.was_successful())
|
|
299
356
|
|
|
300
357
|
|
|
301
358
|
if __name__ == '__main__':
|
scythe_ttp-0.17.0/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.17.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|