Skip to content

Commit 28b0712

Browse files
authored
Merge pull request #11 from FuzzingLabs/fix/remove-erroneous-cli-example
fix: removed erroneous example
2 parents 987c495 + a53d6c9 commit 28b0712

File tree

1 file changed

+91
-62
lines changed

1 file changed

+91
-62
lines changed

cli/src/fuzzforge_cli/main.py

Lines changed: 91 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,24 @@
1212
#
1313
# Additional attribution and requirements are provided in the NOTICE file.
1414

15+
import sys
16+
from typing import List, Optional
1517

1618
import typer
1719
from rich.console import Console
1820
from rich.traceback import install
19-
from typing import Optional, List
20-
import sys
2121

2222
from .commands import (
23-
init,
24-
workflows,
25-
workflow_exec,
23+
ai,
2624
findings,
25+
ingest,
26+
init,
2727
monitor,
28+
workflow_exec,
29+
workflows,
30+
)
31+
from .commands import (
2832
config as config_cmd,
29-
ai,
30-
ingest,
3133
)
3234
from .constants import DEFAULT_VOLUME_MODE
3335
from .fuzzy import enhanced_command_not_found_handler
@@ -78,25 +80,30 @@
7880

7981
# === Top-level commands ===
8082

83+
8184
@app.command()
8285
def init(
8386
name: Optional[str] = typer.Option(
84-
None, "--name", "-n",
85-
help="Project name (defaults to current directory name)"
87+
None, "--name", "-n", help="Project name (defaults to current directory name)"
8688
),
8789
api_url: Optional[str] = typer.Option(
88-
None, "--api-url", "-u",
89-
help="FuzzForge API URL (defaults to http://localhost:8000)"
90+
None,
91+
"--api-url",
92+
"-u",
93+
help="FuzzForge API URL (defaults to http://localhost:8000)",
9094
),
9195
force: bool = typer.Option(
92-
False, "--force", "-f",
93-
help="Force initialization even if project already exists"
94-
)
96+
False,
97+
"--force",
98+
"-f",
99+
help="Force initialization even if project already exists",
100+
),
95101
):
96102
"""
97103
📁 Initialize a new FuzzForge project
98104
"""
99105
from .commands.init import project
106+
100107
project(name=name, api_url=api_url, force=force)
101108

102109

@@ -106,18 +113,18 @@ def status():
106113
📊 Show project and latest execution status
107114
"""
108115
from .commands.status import show_status
116+
109117
show_status()
110118

111119

112120
@app.command()
113121
def config(
114122
key: Optional[str] = typer.Argument(None, help="Configuration key"),
115-
value: Optional[str] = typer.Argument(None, help="Configuration value to set")
123+
value: Optional[str] = typer.Argument(None, help="Configuration value to set"),
116124
):
117125
"""
118126
⚙️ Manage configuration (show all, get, or set values)
119127
"""
120-
from .commands import config as config_cmd
121128

122129
if key is None:
123130
# No arguments: show all config
@@ -133,13 +140,11 @@ def config(
133140
@app.command()
134141
def clean(
135142
days: int = typer.Option(
136-
90, "--days", "-d",
137-
help="Remove data older than this many days"
143+
90, "--days", "-d", help="Remove data older than this many days"
138144
),
139145
dry_run: bool = typer.Option(
140-
False, "--dry-run",
141-
help="Show what would be deleted without actually deleting"
142-
)
146+
False, "--dry-run", help="Show what would be deleted without actually deleting"
147+
),
143148
):
144149
"""
145150
🧹 Clean old execution data and findings
@@ -155,7 +160,9 @@ def clean(
155160
raise typer.Exit(1)
156161

157162
if dry_run:
158-
console.print(f"🔍 [bold]Dry run:[/bold] Would clean data older than {days} days")
163+
console.print(
164+
f"🔍 [bold]Dry run:[/bold] Would clean data older than {days} days"
165+
)
159166

160167
deleted = db.cleanup_old_runs(keep_days=days)
161168

@@ -177,35 +184,41 @@ def clean(
177184
workflow_app.command("info")(workflows.workflow_info)
178185
workflow_app.command("params")(workflows.workflow_parameters)
179186

187+
180188
@workflow_app.command("run")
181189
def run_workflow(
182190
workflow: str = typer.Argument(help="Workflow name"),
183191
target: str = typer.Argument(help="Target path"),
184-
params: List[str] = typer.Argument(default=None, help="Parameters as key=value pairs"),
192+
params: List[str] = typer.Argument(
193+
default=None, help="Parameters as key=value pairs"
194+
),
185195
param_file: Optional[str] = typer.Option(
186-
None, "--param-file", "-f",
187-
help="JSON file containing workflow parameters"
196+
None, "--param-file", "-f", help="JSON file containing workflow parameters"
188197
),
189198
volume_mode: str = typer.Option(
190-
DEFAULT_VOLUME_MODE, "--volume-mode", "-v",
191-
help="Volume mount mode: ro (read-only) or rw (read-write)"
199+
DEFAULT_VOLUME_MODE,
200+
"--volume-mode",
201+
"-v",
202+
help="Volume mount mode: ro (read-only) or rw (read-write)",
192203
),
193204
timeout: Optional[int] = typer.Option(
194-
None, "--timeout", "-t",
195-
help="Execution timeout in seconds"
205+
None, "--timeout", "-t", help="Execution timeout in seconds"
196206
),
197207
interactive: bool = typer.Option(
198-
True, "--interactive/--no-interactive", "-i/-n",
199-
help="Interactive parameter input for missing required parameters"
208+
True,
209+
"--interactive/--no-interactive",
210+
"-i/-n",
211+
help="Interactive parameter input for missing required parameters",
200212
),
201213
wait: bool = typer.Option(
202-
False, "--wait", "-w",
203-
help="Wait for execution to complete"
214+
False, "--wait", "-w", help="Wait for execution to complete"
204215
),
205216
live: bool = typer.Option(
206-
False, "--live", "-l",
207-
help="Start live monitoring after execution (useful for fuzzing workflows)"
208-
)
217+
False,
218+
"--live",
219+
"-l",
220+
help="Start live monitoring after execution (useful for fuzzing workflows)",
221+
),
209222
):
210223
"""
211224
🚀 Execute a security testing workflow
@@ -221,9 +234,10 @@ def run_workflow(
221234
timeout=timeout,
222235
interactive=interactive,
223236
wait=wait,
224-
live=live
237+
live=live,
225238
)
226239

240+
227241
@workflow_app.callback()
228242
def workflow_main():
229243
"""
@@ -239,17 +253,18 @@ def workflow_main():
239253

240254
# === Finding commands (singular) ===
241255

256+
242257
@finding_app.command("export")
243258
def export_finding(
244-
execution_id: Optional[str] = typer.Argument(None, help="Execution ID (defaults to latest)"),
259+
execution_id: Optional[str] = typer.Argument(
260+
None, help="Execution ID (defaults to latest)"
261+
),
245262
format: str = typer.Option(
246-
"sarif", "--format", "-f",
247-
help="Export format: sarif, json, csv"
263+
"sarif", "--format", "-f", help="Export format: sarif, json, csv"
248264
),
249265
output: Optional[str] = typer.Option(
250-
None, "--output", "-o",
251-
help="Output file (defaults to stdout)"
252-
)
266+
None, "--output", "-o", help="Output file (defaults to stdout)"
267+
),
253268
):
254269
"""
255270
📤 Export findings to file
@@ -270,7 +285,9 @@ def export_finding(
270285
execution_id = recent_runs[0].run_id
271286
console.print(f"🔍 Using most recent execution: {execution_id}")
272287
else:
273-
console.print("⚠️ No findings found in project database", style="yellow")
288+
console.print(
289+
"⚠️ No findings found in project database", style="yellow"
290+
)
274291
return
275292
else:
276293
console.print("❌ No project database found", style="red")
@@ -283,14 +300,16 @@ def export_finding(
283300

284301
@finding_app.command("analyze")
285302
def analyze_finding(
286-
finding_id: Optional[str] = typer.Argument(None, help="Finding ID to analyze")
303+
finding_id: Optional[str] = typer.Argument(None, help="Finding ID to analyze"),
287304
):
288305
"""
289306
🤖 AI analysis of a finding
290307
"""
291308
from .commands.ai import analyze_finding as ai_analyze
309+
292310
ai_analyze(finding_id)
293311

312+
294313
@finding_app.callback(invoke_without_command=True)
295314
def finding_main(
296315
ctx: typer.Context,
@@ -309,7 +328,7 @@ def finding_main(
309328
return
310329

311330
# Get remaining arguments for direct viewing
312-
args = ctx.args if hasattr(ctx, 'args') else []
331+
args = ctx.args if hasattr(ctx, "args") else []
313332
finding_id = args[0] if args else None
314333

315334
# Direct viewing: fuzzforge finding [id]
@@ -329,7 +348,9 @@ def finding_main(
329348
finding_id = recent_runs[0].run_id
330349
console.print(f"🔍 Using most recent execution: {finding_id}")
331350
else:
332-
console.print("⚠️ No findings found in project database", style="yellow")
351+
console.print(
352+
"⚠️ No findings found in project database", style="yellow"
353+
)
333354
return
334355
else:
335356
console.print("❌ No project database found", style="red")
@@ -355,6 +376,7 @@ def finding_main(
355376
app.add_typer(ai.app, name="ai", help="🤖 AI integration features")
356377
app.add_typer(ingest.app, name="ingest", help="🧠 Ingest knowledge into AI")
357378

379+
358380
# Help and utility commands
359381
@app.command()
360382
def examples():
@@ -372,7 +394,6 @@ def examples():
372394
[bold]Execute Workflows:[/bold]
373395
ff workflow afl-fuzzing ./target # Run fuzzing on target
374396
ff workflow afl-fuzzing . --live # Run with live monitoring
375-
ff workflow scan-c ./src timeout=300 threads=4 # With parameters
376397
377398
[bold]Monitor Execution:[/bold]
378399
ff status # Check latest execution
@@ -399,16 +420,16 @@ def version():
399420
📦 Show version information
400421
"""
401422
from . import __version__
423+
402424
console.print(f"FuzzForge CLI v{__version__}")
403-
console.print(f"Short command: ff")
425+
console.print("Short command: ff")
404426

405427

406428
@app.callback()
407429
def main_callback(
408430
ctx: typer.Context,
409431
version: Optional[bool] = typer.Option(
410-
None, "--version", "-v",
411-
help="Show version information"
432+
None, "--version", "-v", help="Show version information"
412433
),
413434
):
414435
"""
@@ -422,6 +443,7 @@ def main_callback(
422443
"""
423444
if version:
424445
from . import __version__
446+
425447
console.print(f"FuzzForge CLI v{__version__}")
426448
raise typer.Exit()
427449

@@ -432,12 +454,11 @@ def main():
432454
if len(sys.argv) > 1:
433455
args = sys.argv[1:]
434456

435-
436457
# Handle finding command with pattern recognition
437-
if len(args) >= 2 and args[0] == 'finding':
438-
finding_subcommands = ['export', 'analyze']
458+
if len(args) >= 2 and args[0] == "finding":
459+
finding_subcommands = ["export", "analyze"]
439460
# Skip custom dispatching if help flags are present
440-
if not any(arg in ['--help', '-h', '--version', '-v'] for arg in args):
461+
if not any(arg in ["--help", "-h", "--version", "-v"] for arg in args):
441462
if args[1] not in finding_subcommands:
442463
# Direct finding display: ff finding <id>
443464
from .commands.findings import get_findings
@@ -457,18 +478,26 @@ def main():
457478
app()
458479
except SystemExit as e:
459480
# Enhanced error handling for command not found
460-
if hasattr(e, 'code') and e.code != 0 and len(sys.argv) > 1:
481+
if hasattr(e, "code") and e.code != 0 and len(sys.argv) > 1:
461482
command_parts = sys.argv[1:]
462-
clean_parts = [part for part in command_parts if not part.startswith('-')]
483+
clean_parts = [part for part in command_parts if not part.startswith("-")]
463484

464485
if clean_parts:
465486
main_cmd = clean_parts[0]
466487
valid_commands = [
467-
'init', 'status', 'config', 'clean',
468-
'workflows', 'workflow',
469-
'findings', 'finding',
470-
'monitor', 'ai', 'ingest',
471-
'examples', 'version'
488+
"init",
489+
"status",
490+
"config",
491+
"clean",
492+
"workflows",
493+
"workflow",
494+
"findings",
495+
"finding",
496+
"monitor",
497+
"ai",
498+
"ingest",
499+
"examples",
500+
"version",
472501
]
473502

474503
if main_cmd not in valid_commands:

0 commit comments

Comments
 (0)