scythe-ttp 0.15.10__tar.gz → 0.16.0__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.15.10/scythe_ttp.egg-info → scythe_ttp-0.16.0}/PKG-INFO +1 -1
- scythe_ttp-0.16.0/VERSION +1 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/journeys/actions.py +29 -7
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0/scythe_ttp.egg-info}/PKG-INFO +1 -1
- scythe_ttp-0.15.10/VERSION +0 -1
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/LICENSE +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/MANIFEST.in +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/README.md +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/pyproject.toml +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/requirements.txt +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/__init__.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/auth/__init__.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/auth/base.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/auth/basic.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/auth/bearer.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/auth/cookie_jwt.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/behaviors/__init__.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/behaviors/base.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/behaviors/default.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/behaviors/human.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/behaviors/machine.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/behaviors/stealth.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/cli/__init__.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/cli/main.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/core/__init__.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/core/executor.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/core/headers.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/core/ttp.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/journeys/__init__.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/journeys/base.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/journeys/executor.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/orchestrators/__init__.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/orchestrators/base.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/orchestrators/batch.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/orchestrators/distributed.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/orchestrators/scale.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/payloads/__init__.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/payloads/generators.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/ttps/__init__.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/ttps/web/__init__.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/ttps/web/login_bruteforce.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/ttps/web/sql_injection.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe/ttps/web/uuid_guessing.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe_ttp.egg-info/SOURCES.txt +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe_ttp.egg-info/dependency_links.txt +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe_ttp.egg-info/entry_points.txt +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe_ttp.egg-info/requires.txt +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/scythe_ttp.egg-info/top_level.txt +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/setup.cfg +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/tests/test_api_models.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/tests/test_authentication.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/tests/test_behaviors.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/tests/test_cli.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/tests/test_cookie_jwt_auth.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/tests/test_expected_results.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/tests/test_feature_completeness.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/tests/test_header_extraction.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/tests/test_journeys.py +0 -0
- {scythe_ttp-0.15.10 → scythe_ttp-0.16.0}/tests/test_orchestrators.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.16.0
|
|
@@ -659,6 +659,7 @@ class ApiRequestAction(Action):
|
|
|
659
659
|
def __init__(self,
|
|
660
660
|
method: str,
|
|
661
661
|
url: str,
|
|
662
|
+
flush: bool = False,
|
|
662
663
|
params: Optional[Dict[str, Any]] = None,
|
|
663
664
|
body_json: Optional[Dict[str, Any]] = None,
|
|
664
665
|
data: Optional[Dict[str, Any]] = None,
|
|
@@ -673,6 +674,7 @@ class ApiRequestAction(Action):
|
|
|
673
674
|
fail_on_validation_error: bool = False):
|
|
674
675
|
self.method = method.upper()
|
|
675
676
|
self.url = url
|
|
677
|
+
self.flush = flush
|
|
676
678
|
self.params = params or {}
|
|
677
679
|
self.body_json = body_json
|
|
678
680
|
self.data = data
|
|
@@ -692,15 +694,15 @@ class ApiRequestAction(Action):
|
|
|
692
694
|
if session is None:
|
|
693
695
|
session = requests.Session()
|
|
694
696
|
context['requests_session'] = session
|
|
695
|
-
|
|
696
|
-
# Build headers: auth headers from context
|
|
697
|
+
|
|
698
|
+
# Build headers: auth headers from context and action headers (action overrides)
|
|
697
699
|
final_headers = {}
|
|
698
700
|
auth_headers = context.get('auth_headers', {}) or {}
|
|
699
701
|
if auth_headers:
|
|
700
702
|
final_headers.update(auth_headers)
|
|
701
703
|
if self.headers:
|
|
702
704
|
final_headers.update(self.headers)
|
|
703
|
-
|
|
705
|
+
|
|
704
706
|
# Simple masking for sensitive headers
|
|
705
707
|
def _mask_headers(headers: Dict[str, Any]) -> Dict[str, Any]:
|
|
706
708
|
masked = {}
|
|
@@ -713,7 +715,7 @@ class ApiRequestAction(Action):
|
|
|
713
715
|
else:
|
|
714
716
|
masked[k] = v
|
|
715
717
|
return masked
|
|
716
|
-
|
|
718
|
+
|
|
717
719
|
# Resolve URL: absolute or join with target_url from context
|
|
718
720
|
from urllib.parse import urljoin
|
|
719
721
|
from ..core.headers import HeaderExtractor
|
|
@@ -725,7 +727,7 @@ class ApiRequestAction(Action):
|
|
|
725
727
|
resolved_url = self.url
|
|
726
728
|
else:
|
|
727
729
|
resolved_url = urljoin(base_url, self.url)
|
|
728
|
-
|
|
730
|
+
|
|
729
731
|
# Store request details early
|
|
730
732
|
self.store_result('request_method', self.method)
|
|
731
733
|
self.store_result('url', resolved_url)
|
|
@@ -736,7 +738,7 @@ class ApiRequestAction(Action):
|
|
|
736
738
|
if self.data is not None:
|
|
737
739
|
self.store_result('request_data', self.data)
|
|
738
740
|
self.store_result('request_headers', _mask_headers(final_headers))
|
|
739
|
-
|
|
741
|
+
|
|
740
742
|
logger = logging.getLogger("Journey.ApiRequestAction")
|
|
741
743
|
# Honor any pending rate-limit resume time set by previous actions/steps
|
|
742
744
|
try:
|
|
@@ -861,14 +863,25 @@ class ApiRequestAction(Action):
|
|
|
861
863
|
|
|
862
864
|
# Determine success (status-based by default)
|
|
863
865
|
if self.expected_status is not None:
|
|
864
|
-
http_ok = (
|
|
866
|
+
http_ok = (status_code == self.expected_status)
|
|
867
|
+
if not http_ok:
|
|
868
|
+
self.store_result('status_mismatch', f"Expected status {self.expected_status}, got {status_code}")
|
|
869
|
+
logger.warning(f"API request status mismatch: expected {self.expected_status}, got {status_code}")
|
|
865
870
|
else:
|
|
866
871
|
http_ok = bool(getattr(response, 'ok', False))
|
|
867
872
|
|
|
873
|
+
# Store the final status check result
|
|
874
|
+
self.store_result('http_status_ok', http_ok)
|
|
875
|
+
|
|
868
876
|
# Optionally fail on validation error
|
|
869
877
|
if self.response_model is not None and self.fail_on_validation_error and self.get_result('response_validation_error'):
|
|
870
878
|
return False
|
|
871
879
|
|
|
880
|
+
if self.flush:
|
|
881
|
+
clear_requests_session(context)
|
|
882
|
+
|
|
883
|
+
# Return False if status doesn't match expected, regardless of expected_result
|
|
884
|
+
# The framework will then compare this with expected_result to determine test outcome
|
|
872
885
|
return http_ok
|
|
873
886
|
except Exception as e:
|
|
874
887
|
last_exception = e
|
|
@@ -878,3 +891,12 @@ class ApiRequestAction(Action):
|
|
|
878
891
|
|
|
879
892
|
# If we got here and had an exception or no return, fail
|
|
880
893
|
return False
|
|
894
|
+
|
|
895
|
+
def clear_requests_session(context: Dict[str, Any]):
|
|
896
|
+
"""Clear the request session from the context."""
|
|
897
|
+
logger = logging.getLogger("Journey.ApiRequestAction")
|
|
898
|
+
session = context.get('requests_session')
|
|
899
|
+
if session is not None:
|
|
900
|
+
session.close()
|
|
901
|
+
context['requests_session'] = None
|
|
902
|
+
logger.info("Cleared requests session from context")
|
scythe_ttp-0.15.10/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.15.10
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|