scythe-ttp 0.17.5__tar.gz → 0.17.7__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 (61) hide show
  1. {scythe_ttp-0.17.5/scythe_ttp.egg-info → scythe_ttp-0.17.7}/PKG-INFO +1 -1
  2. scythe_ttp-0.17.7/VERSION +1 -0
  3. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/cli/main.py +51 -13
  4. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/core/executor.py +9 -0
  5. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/journeys/executor.py +9 -0
  6. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/orchestrators/base.py +18 -0
  7. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7/scythe_ttp.egg-info}/PKG-INFO +1 -1
  8. scythe_ttp-0.17.5/VERSION +0 -1
  9. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/LICENSE +0 -0
  10. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/MANIFEST.in +0 -0
  11. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/README.md +0 -0
  12. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/pyproject.toml +0 -0
  13. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/requirements.txt +0 -0
  14. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/__init__.py +0 -0
  15. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/auth/__init__.py +0 -0
  16. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/auth/base.py +0 -0
  17. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/auth/basic.py +0 -0
  18. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/auth/bearer.py +0 -0
  19. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/auth/cookie_jwt.py +0 -0
  20. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/behaviors/__init__.py +0 -0
  21. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/behaviors/base.py +0 -0
  22. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/behaviors/default.py +0 -0
  23. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/behaviors/human.py +0 -0
  24. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/behaviors/machine.py +0 -0
  25. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/behaviors/stealth.py +0 -0
  26. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/cli/__init__.py +0 -0
  27. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/core/__init__.py +0 -0
  28. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/core/headers.py +0 -0
  29. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/core/ttp.py +0 -0
  30. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/journeys/__init__.py +0 -0
  31. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/journeys/actions.py +0 -0
  32. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/journeys/base.py +0 -0
  33. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/orchestrators/__init__.py +0 -0
  34. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/orchestrators/batch.py +0 -0
  35. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/orchestrators/distributed.py +0 -0
  36. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/orchestrators/scale.py +0 -0
  37. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/payloads/__init__.py +0 -0
  38. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/payloads/generators.py +0 -0
  39. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/ttps/__init__.py +0 -0
  40. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/ttps/web/__init__.py +0 -0
  41. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/ttps/web/login_bruteforce.py +0 -0
  42. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/ttps/web/sql_injection.py +0 -0
  43. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe/ttps/web/uuid_guessing.py +0 -0
  44. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe_ttp.egg-info/SOURCES.txt +0 -0
  45. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe_ttp.egg-info/dependency_links.txt +0 -0
  46. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe_ttp.egg-info/entry_points.txt +0 -0
  47. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe_ttp.egg-info/requires.txt +0 -0
  48. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/scythe_ttp.egg-info/top_level.txt +0 -0
  49. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/setup.cfg +0 -0
  50. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/tests/test_api_models.py +0 -0
  51. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/tests/test_authentication.py +0 -0
  52. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/tests/test_behaviors.py +0 -0
  53. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/tests/test_cli.py +0 -0
  54. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/tests/test_cookie_jwt_auth.py +0 -0
  55. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/tests/test_executor_modes.py +0 -0
  56. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/tests/test_expected_results.py +0 -0
  57. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/tests/test_feature_completeness.py +0 -0
  58. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/tests/test_header_extraction.py +0 -0
  59. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/tests/test_journeys.py +0 -0
  60. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/tests/test_orchestrators.py +0 -0
  61. {scythe_ttp-0.17.5 → scythe_ttp-0.17.7}/tests/test_ttp_api_mode.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scythe-ttp
3
- Version: 0.17.5
3
+ Version: 0.17.7
4
4
  Summary: An extensible framework for emulating attacker TTPs with Selenium.
5
5
  Author-email: EpykLab <cyber@epyklab.com>
6
6
  Classifier: Programming Language :: Python :: 3
@@ -0,0 +1 @@
1
+ 0.17.7
@@ -68,20 +68,46 @@ 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
-
71
+
72
72
  # Example usage with TTPExecutor:
73
73
  # from scythe.core.executor import TTPExecutor
74
- # executor = TTPExecutor(ttp=my_ttp, target_url=args.url)
74
+ # from scythe.ttps.web.login_bruteforce import LoginBruteforceTTP
75
+ #
76
+ # ttp = LoginBruteforceTTP(
77
+ # payloads=['admin', 'root', 'test'],
78
+ # expected_result=False # Expect security controls to block attempts
79
+ # )
80
+ # executor = TTPExecutor(ttp=ttp, target_url=args.url)
75
81
  # executor.run()
76
82
  # return executor.was_successful() # Returns True if all results matched expectations
77
-
83
+
78
84
  # Example usage with JourneyExecutor:
79
85
  # from scythe.journeys.executor import JourneyExecutor
80
- # executor = JourneyExecutor(journey=my_journey, target_url=args.url)
86
+ # from scythe.journeys.base import Journey, Step
87
+ # from scythe.journeys.actions import NavigateAction, FillFormAction, ClickAction
88
+ #
89
+ # journey = Journey(
90
+ # name="Login Journey",
91
+ # description="Test user login flow",
92
+ # expected_result=True # Expect journey to succeed
93
+ # )
94
+ # journey.add_step(Step("Navigate").add_action(NavigateAction(url=args.url)))
95
+ # executor = JourneyExecutor(journey=journey, target_url=args.url)
81
96
  # executor.run()
82
97
  # return executor.was_successful() # Returns True if journey succeeded as expected
83
-
84
- return True
98
+
99
+ # Example usage with Orchestrators:
100
+ # from scythe.orchestrators.scale import ScaleOrchestrator
101
+ # from scythe.orchestrators.base import OrchestrationStrategy
102
+ #
103
+ # orchestrator = ScaleOrchestrator(
104
+ # strategy=OrchestrationStrategy.PARALLEL,
105
+ # max_workers=10
106
+ # )
107
+ # result = orchestrator.orchestrate_ttp(ttp=my_ttp, target_url=args.url, replications=100)
108
+ # return orchestrator.exit_code(result) == 0 # Returns True if all executions succeeded
109
+
110
+ return executor.exit_code() # assumes executor var
85
111
 
86
112
 
87
113
  def main():
@@ -233,14 +259,14 @@ def main():
233
259
  if check_url_available(args.url):
234
260
  if args.gate_versions:
235
261
  if check_version_in_response_header(args):
236
- ok = scythe_test_definition(args)
237
- sys.exit(0 if ok else 1)
262
+ exit_code = scythe_test_definition(args)
263
+ sys.exit(exit_code)
238
264
  else:
239
265
  print("No compatible version found in response header.")
240
266
  sys.exit(1)
241
267
  else:
242
- ok = scythe_test_definition(args)
243
- sys.exit(0 if ok else 1)
268
+ exit_code = scythe_test_definition(args)
269
+ sys.exit(exit_code)
244
270
  else:
245
271
  print("URL not available.")
246
272
  sys.exit(1)
@@ -254,6 +280,13 @@ class ScytheCLIError(Exception):
254
280
  pass
255
281
 
256
282
 
283
+ class ExitWithCode(Exception):
284
+ """Exception to exit with a specific code from within Typer commands."""
285
+ def __init__(self, code: int):
286
+ self.code = code
287
+ super().__init__()
288
+
289
+
257
290
  def _find_project_root(start: Optional[str] = None) -> Optional[str]:
258
291
  """Walk upwards from start (or cwd) to find a directory containing .scythe."""
259
292
  cur = os.path.abspath(start or os.getcwd())
@@ -702,7 +735,10 @@ def main(argv: Optional[List[str]] = None) -> int:
702
735
  code, output, version = _run_test(project_root, name, extra)
703
736
  _record_run(project_root, name, code, output, version)
704
737
  print(output)
705
- return code
738
+ # Raise exception to propagate exit code through Typer
739
+ if code != 0:
740
+ raise ExitWithCode(code)
741
+ return 0
706
742
 
707
743
  db_app = typer.Typer(
708
744
  no_args_is_help=True,
@@ -737,8 +773,10 @@ def main(argv: Optional[List[str]] = None) -> int:
737
773
  app.add_typer(db_app, name="db")
738
774
 
739
775
  try:
740
- rv = app()
741
- return int(rv) if isinstance(rv, int) else 0
776
+ app()
777
+ return 0
778
+ except ExitWithCode as e:
779
+ return e.code
742
780
  except ScytheCLIError as e:
743
781
  print(f"Error: {e}", file=sys.stderr)
744
782
  return 2
@@ -344,3 +344,12 @@ class TTPExecutor:
344
344
  True if all test results matched expectations, False otherwise
345
345
  """
346
346
  return not self.has_test_failures
347
+
348
+ def exit_code(self) -> int:
349
+ """
350
+ Get the exit code for this test execution.
351
+
352
+ Returns:
353
+ 0 if test was successful (results matched expectations), 1 otherwise
354
+ """
355
+ return 0 if self.was_successful() else 1
@@ -463,6 +463,15 @@ class JourneyExecutor:
463
463
  actual = self.execution_results.get('overall_success', False)
464
464
  expected = self.execution_results.get('expected_result', True)
465
465
  return actual == expected
466
+
467
+ def exit_code(self) -> int:
468
+ """
469
+ Get the exit code for this journey execution.
470
+
471
+ Returns:
472
+ 0 if journey was successful (results matched expectations), 1 otherwise
473
+ """
474
+ return 0 if self.was_successful() else 1
466
475
 
467
476
 
468
477
  class JourneyRunner:
@@ -281,6 +281,24 @@ class Orchestrator(ABC):
281
281
  self.logger.warning(f" ... and {len(result.errors) - 3} more errors")
282
282
 
283
283
  self.logger.info("="*60)
284
+
285
+ def exit_code(self, result: OrchestrationResult) -> int:
286
+ """
287
+ Get the exit code for an orchestration result.
288
+
289
+ An orchestration is considered successful if all executions completed
290
+ successfully (matching their expected results).
291
+
292
+ Args:
293
+ result: OrchestrationResult to evaluate
294
+
295
+ Returns:
296
+ 0 if all executions were successful, 1 otherwise
297
+ """
298
+ # Check if any executions failed or if there were errors
299
+ if result.failed_executions > 0 or len(result.errors) > 0:
300
+ return 1
301
+ return 0
284
302
 
285
303
 
286
304
  class ExecutionContext:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scythe-ttp
3
- Version: 0.17.5
3
+ Version: 0.17.7
4
4
  Summary: An extensible framework for emulating attacker TTPs with Selenium.
5
5
  Author-email: EpykLab <cyber@epyklab.com>
6
6
  Classifier: Programming Language :: Python :: 3
scythe_ttp-0.17.5/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.17.5
File without changes
File without changes
File without changes
File without changes
File without changes