scythe-ttp 0.17.5__py3-none-any.whl → 0.17.7__py3-none-any.whl
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/cli/main.py +51 -13
- scythe/core/executor.py +9 -0
- scythe/journeys/executor.py +9 -0
- scythe/orchestrators/base.py +18 -0
- {scythe_ttp-0.17.5.dist-info → scythe_ttp-0.17.7.dist-info}/METADATA +1 -1
- {scythe_ttp-0.17.5.dist-info → scythe_ttp-0.17.7.dist-info}/RECORD +10 -10
- {scythe_ttp-0.17.5.dist-info → scythe_ttp-0.17.7.dist-info}/WHEEL +0 -0
- {scythe_ttp-0.17.5.dist-info → scythe_ttp-0.17.7.dist-info}/entry_points.txt +0 -0
- {scythe_ttp-0.17.5.dist-info → scythe_ttp-0.17.7.dist-info}/licenses/LICENSE +0 -0
- {scythe_ttp-0.17.5.dist-info → scythe_ttp-0.17.7.dist-info}/top_level.txt +0 -0
scythe/cli/main.py
CHANGED
|
@@ -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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
237
|
-
sys.exit(
|
|
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
|
-
|
|
243
|
-
sys.exit(
|
|
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
|
-
|
|
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
|
-
|
|
741
|
-
return
|
|
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
|
scythe/core/executor.py
CHANGED
|
@@ -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
|
scythe/journeys/executor.py
CHANGED
|
@@ -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:
|
scythe/orchestrators/base.py
CHANGED
|
@@ -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:
|
|
@@ -11,17 +11,17 @@ scythe/behaviors/human.py,sha256=1PqYvE7cnxlj-KDmDIr3uzfWHvDAbbxQxJ0V0iTl9yo,102
|
|
|
11
11
|
scythe/behaviors/machine.py,sha256=NDMUq3mDhpCvujzAFxhn2eSVq78-J-LSBhIcvHkzKXo,4624
|
|
12
12
|
scythe/behaviors/stealth.py,sha256=xv7MrPQgRCdCUJyYTcXV2aasWZoAw8rAQWg-AuQVb7U,15278
|
|
13
13
|
scythe/cli/__init__.py,sha256=9EVxmFiWsAoqWJ6br1bc3BxlA71JyOQP28fUHhX2k7E,43
|
|
14
|
-
scythe/cli/main.py,sha256=
|
|
14
|
+
scythe/cli/main.py,sha256=4Hcx0TkuTMC3gX3Z7k8ZgusW5TfbeJaCJpvczqu9LVU,27481
|
|
15
15
|
scythe/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
-
scythe/core/executor.py,sha256=
|
|
16
|
+
scythe/core/executor.py,sha256=F8r_t5vSvsxNuUFx-Y0JzSe051cj_w42iyZrS7wocb4,16306
|
|
17
17
|
scythe/core/headers.py,sha256=AokCQ3F5QGUcfoK7iO57hA1HHL4IznZeWV464_MqYcE,16670
|
|
18
18
|
scythe/core/ttp.py,sha256=tEYIhDdr8kcwQrlcfVmdeLFiAfOvc0BhPOVxPh8TiWo,5676
|
|
19
19
|
scythe/journeys/__init__.py,sha256=Odi8NhRg7Hefmo1EJj1guakrCSPhsuus4i-_62uUUjs,654
|
|
20
20
|
scythe/journeys/actions.py,sha256=URr53p1GQxSIBZo0IubchQ1dlfvnPHgCtmkRfLSoi7A,40333
|
|
21
21
|
scythe/journeys/base.py,sha256=vXIgEnSW__iYTriBbuMG4l_XCM96xojJH_fyFScKoBY,24969
|
|
22
|
-
scythe/journeys/executor.py,sha256=
|
|
22
|
+
scythe/journeys/executor.py,sha256=wSAFFU9qwyYA2Q_5TZBUDgafVb1slwvj0VNJ7cR46FE,25223
|
|
23
23
|
scythe/orchestrators/__init__.py,sha256=_vemcXjKbB1jI0F2dPA0F1zNsyUekjcXImLDUDhWDN0,560
|
|
24
|
-
scythe/orchestrators/base.py,sha256=
|
|
24
|
+
scythe/orchestrators/base.py,sha256=oZ68VmX18mBITvFUXfHJYvIE20PCfEAKrsikdr6_0Qg,14219
|
|
25
25
|
scythe/orchestrators/batch.py,sha256=FpK501kk-earJzz6v7dcuw2y708rTvt_IMH_5qjKdrc,26635
|
|
26
26
|
scythe/orchestrators/distributed.py,sha256=ts19NZzPfr0ouFbUFrktPO-iL5jBNB57mcOh0eDJDmE,33554
|
|
27
27
|
scythe/orchestrators/scale.py,sha256=l2-4U6ISeBDBZz7CG6ef9W-zyF6LiAM4uXXKlfczLB0,21394
|
|
@@ -32,9 +32,9 @@ scythe/ttps/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
32
32
|
scythe/ttps/web/login_bruteforce.py,sha256=ybmN2Vl9-p58YbOchirY1193GvlaTmUTW1qlluN_l3I,7816
|
|
33
33
|
scythe/ttps/web/sql_injection.py,sha256=rIRHaRUilSrMA5q5MO1RqR6-TM_fRIiCanPaFz5wKKs,11712
|
|
34
34
|
scythe/ttps/web/uuid_guessing.py,sha256=JwNt_9HVynMWFPPU6UGJFcpxvMVDsvc_wAnJVtcYbps,1235
|
|
35
|
-
scythe_ttp-0.17.
|
|
36
|
-
scythe_ttp-0.17.
|
|
37
|
-
scythe_ttp-0.17.
|
|
38
|
-
scythe_ttp-0.17.
|
|
39
|
-
scythe_ttp-0.17.
|
|
40
|
-
scythe_ttp-0.17.
|
|
35
|
+
scythe_ttp-0.17.7.dist-info/licenses/LICENSE,sha256=B7iB4Fv6zDQolC7IgqNF8F4GEp_DLe2jrPPuR_MYMOM,1064
|
|
36
|
+
scythe_ttp-0.17.7.dist-info/METADATA,sha256=pgGUSFXUFO_HBHx1OqyLlOZDZQhDTsgoNKB5QJYw2iM,30188
|
|
37
|
+
scythe_ttp-0.17.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
38
|
+
scythe_ttp-0.17.7.dist-info/entry_points.txt,sha256=rAAsFBcCm0OX3I4uRyclfx4YJGoTuumZKY43HN7R5Ro,48
|
|
39
|
+
scythe_ttp-0.17.7.dist-info/top_level.txt,sha256=BCKTrPuVvmLyhOR07C1ggOh6sU7g2LoVvwDMn46O55Y,7
|
|
40
|
+
scythe_ttp-0.17.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|