mirror of
https://github.com/FuzzingLabs/fuzzforge_ai.git
synced 2026-02-13 15:52:46 +00:00
Merge branch 'master' into dev for v0.7.0 release
Resolved conflicts: - Kept monitor.py (dev version - required for live monitoring) - Kept workflow_exec.py (dev version - includes worker management, --live, --fail-on, --export-sarif) - Kept main.py (dev version - includes new command structure) All conflicts resolved in favor of dev branch features for 0.7.0 release.
This commit is contained in:
@@ -10,11 +10,10 @@
|
||||
#
|
||||
# Additional attribution and requirements are provided in the NOTICE file.
|
||||
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
import os
|
||||
from pathlib import Path
|
||||
from textwrap import dedent
|
||||
from typing import Optional
|
||||
|
||||
@@ -32,17 +31,20 @@ app = typer.Typer()
|
||||
@app.command()
|
||||
def project(
|
||||
name: Optional[str] = typer.Option(
|
||||
None, "--name", "-n",
|
||||
help="Project name (defaults to current directory name)"
|
||||
None, "--name", "-n", help="Project name (defaults to current directory name)"
|
||||
),
|
||||
api_url: Optional[str] = typer.Option(
|
||||
None, "--api-url", "-u",
|
||||
help="FuzzForge API URL (defaults to http://localhost:8000)"
|
||||
None,
|
||||
"--api-url",
|
||||
"-u",
|
||||
help="FuzzForge API URL (defaults to http://localhost:8000)",
|
||||
),
|
||||
force: bool = typer.Option(
|
||||
False, "--force", "-f",
|
||||
help="Force initialization even if project already exists"
|
||||
)
|
||||
False,
|
||||
"--force",
|
||||
"-f",
|
||||
help="Force initialization even if project already exists",
|
||||
),
|
||||
):
|
||||
"""
|
||||
📁 Initialize a new FuzzForge project in the current directory.
|
||||
@@ -58,24 +60,20 @@ def project(
|
||||
# Check if project already exists
|
||||
if fuzzforge_dir.exists() and not force:
|
||||
if fuzzforge_dir.is_dir() and any(fuzzforge_dir.iterdir()):
|
||||
console.print("❌ FuzzForge project already exists in this directory", style="red")
|
||||
console.print(
|
||||
"❌ FuzzForge project already exists in this directory", style="red"
|
||||
)
|
||||
console.print("Use --force to reinitialize", style="dim")
|
||||
raise typer.Exit(1)
|
||||
|
||||
# Get project name
|
||||
if not name:
|
||||
name = Prompt.ask(
|
||||
"Project name",
|
||||
default=current_dir.name,
|
||||
console=console
|
||||
)
|
||||
name = Prompt.ask("Project name", default=current_dir.name, console=console)
|
||||
|
||||
# Get API URL
|
||||
if not api_url:
|
||||
api_url = Prompt.ask(
|
||||
"FuzzForge API URL",
|
||||
default="http://localhost:8000",
|
||||
console=console
|
||||
"FuzzForge API URL", default="http://localhost:8000", console=console
|
||||
)
|
||||
|
||||
# Confirm initialization
|
||||
@@ -117,15 +115,15 @@ def project(
|
||||
]
|
||||
|
||||
if gitignore_path.exists():
|
||||
with open(gitignore_path, 'r') as f:
|
||||
with open(gitignore_path, "r") as f:
|
||||
existing_content = f.read()
|
||||
|
||||
if "# FuzzForge CLI" not in existing_content:
|
||||
with open(gitignore_path, 'a') as f:
|
||||
with open(gitignore_path, "a") as f:
|
||||
f.write(f"\n{chr(10).join(gitignore_entries)}\n")
|
||||
console.print("📝 Updated .gitignore with FuzzForge entries")
|
||||
else:
|
||||
with open(gitignore_path, 'w') as f:
|
||||
with open(gitignore_path, "w") as f:
|
||||
f.write(f"{chr(10).join(gitignore_entries)}\n")
|
||||
console.print("📝 Created .gitignore")
|
||||
|
||||
@@ -145,9 +143,6 @@ fuzzforge workflows
|
||||
# Submit a workflow for analysis
|
||||
fuzzforge workflow <workflow-name> /path/to/target
|
||||
|
||||
# Monitor run progress
|
||||
fuzzforge monitor live <run-id>
|
||||
|
||||
# View findings
|
||||
fuzzforge finding <run-id>
|
||||
```
|
||||
@@ -159,7 +154,7 @@ fuzzforge finding <run-id>
|
||||
- `.fuzzforge/findings.db` - Local database for runs and findings
|
||||
"""
|
||||
|
||||
with open(readme_path, 'w') as f:
|
||||
with open(readme_path, "w") as f:
|
||||
f.write(readme_content)
|
||||
console.print("📚 Created README.md")
|
||||
|
||||
|
||||
@@ -14,9 +14,9 @@ Provides "Did you mean...?" functionality and intelligent command/parameter sugg
|
||||
#
|
||||
# Additional attribution and requirements are provided in the NOTICE file.
|
||||
|
||||
|
||||
import difflib
|
||||
from typing import List, Optional, Dict, Any, Tuple
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
from rich.console import Console
|
||||
from rich.panel import Panel
|
||||
from rich.text import Text
|
||||
@@ -34,10 +34,9 @@ class FuzzyMatcher:
|
||||
"workflows": ["list", "info"],
|
||||
"runs": ["submit", "status", "list", "rerun"],
|
||||
"findings": ["get", "list", "export", "all"],
|
||||
"monitor": ["stats", "crashes", "live"],
|
||||
"config": ["set", "get", "list", "init"],
|
||||
"ai": ["ask", "summarize", "explain"],
|
||||
"ingest": ["project", "findings"]
|
||||
"ingest": ["project", "findings"],
|
||||
}
|
||||
|
||||
# Common workflow names
|
||||
@@ -47,7 +46,7 @@ class FuzzyMatcher:
|
||||
"infrastructure_scan",
|
||||
"static_analysis_scan",
|
||||
"penetration_testing_scan",
|
||||
"secret_detection_scan"
|
||||
"secret_detection_scan",
|
||||
]
|
||||
|
||||
# Common parameter names
|
||||
@@ -60,24 +59,25 @@ class FuzzyMatcher:
|
||||
"param-file",
|
||||
"interactive",
|
||||
"wait",
|
||||
"live",
|
||||
"format",
|
||||
"output",
|
||||
"severity",
|
||||
"since",
|
||||
"limit",
|
||||
"stats",
|
||||
"export"
|
||||
"export",
|
||||
]
|
||||
|
||||
# Common values
|
||||
self.common_values = {
|
||||
"volume_mode": ["ro", "rw"],
|
||||
"format": ["json", "csv", "html", "sarif"],
|
||||
"severity": ["critical", "high", "medium", "low", "info"]
|
||||
"severity": ["critical", "high", "medium", "low", "info"],
|
||||
}
|
||||
|
||||
def find_closest_command(self, user_input: str, command_group: Optional[str] = None) -> Optional[Tuple[str, float]]:
|
||||
def find_closest_command(
|
||||
self, user_input: str, command_group: Optional[str] = None
|
||||
) -> Optional[Tuple[str, float]]:
|
||||
"""Find the closest matching command."""
|
||||
if command_group and command_group in self.commands:
|
||||
# Search within a specific command group
|
||||
@@ -86,9 +86,7 @@ class FuzzyMatcher:
|
||||
# Search all main commands
|
||||
candidates = list(self.commands.keys())
|
||||
|
||||
matches = difflib.get_close_matches(
|
||||
user_input, candidates, n=1, cutoff=0.6
|
||||
)
|
||||
matches = difflib.get_close_matches(user_input, candidates, n=1, cutoff=0.6)
|
||||
|
||||
if matches:
|
||||
match = matches[0]
|
||||
@@ -114,7 +112,7 @@ class FuzzyMatcher:
|
||||
def find_closest_parameter(self, user_input: str) -> Optional[Tuple[str, float]]:
|
||||
"""Find the closest matching parameter name."""
|
||||
# Remove leading dashes
|
||||
clean_input = user_input.lstrip('-')
|
||||
clean_input = user_input.lstrip("-")
|
||||
|
||||
matches = difflib.get_close_matches(
|
||||
clean_input, self.parameter_names, n=1, cutoff=0.6
|
||||
@@ -139,7 +137,9 @@ class FuzzyMatcher:
|
||||
|
||||
return []
|
||||
|
||||
def get_command_suggestions(self, user_command: List[str]) -> Optional[Dict[str, Any]]:
|
||||
def get_command_suggestions(
|
||||
self, user_command: List[str]
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
"""Get suggestions for a user command that may have typos."""
|
||||
if not user_command:
|
||||
return None
|
||||
@@ -153,11 +153,9 @@ class FuzzyMatcher:
|
||||
if closest:
|
||||
match, confidence = closest
|
||||
suggestions["type"] = "main_command"
|
||||
suggestions["suggestions"].append({
|
||||
"text": match,
|
||||
"confidence": confidence,
|
||||
"type": "command"
|
||||
})
|
||||
suggestions["suggestions"].append(
|
||||
{"text": match, "confidence": confidence, "type": "command"}
|
||||
)
|
||||
|
||||
# Check subcommand if present
|
||||
elif len(user_command) > 1:
|
||||
@@ -167,11 +165,13 @@ class FuzzyMatcher:
|
||||
if closest:
|
||||
match, confidence = closest
|
||||
suggestions["type"] = "subcommand"
|
||||
suggestions["suggestions"].append({
|
||||
"text": f"{main_cmd} {match}",
|
||||
"confidence": confidence,
|
||||
"type": "subcommand"
|
||||
})
|
||||
suggestions["suggestions"].append(
|
||||
{
|
||||
"text": f"{main_cmd} {match}",
|
||||
"confidence": confidence,
|
||||
"type": "subcommand",
|
||||
}
|
||||
)
|
||||
|
||||
return suggestions if suggestions["suggestions"] else None
|
||||
|
||||
@@ -210,17 +210,19 @@ def display_command_suggestion(suggestions: Dict[str, Any]):
|
||||
|
||||
# Add helpful context
|
||||
if suggestion_type == "main_command":
|
||||
text.append("\n💡 Use 'fuzzforge --help' to see all available commands", style="dim")
|
||||
text.append(
|
||||
"\n💡 Use 'fuzzforge --help' to see all available commands", style="dim"
|
||||
)
|
||||
elif suggestion_type == "subcommand":
|
||||
main_cmd = suggestions["original"][0]
|
||||
text.append(f"\n💡 Use 'fuzzforge {main_cmd} --help' to see available subcommands", style="dim")
|
||||
text.append(
|
||||
f"\n💡 Use 'fuzzforge {main_cmd} --help' to see available subcommands",
|
||||
style="dim",
|
||||
)
|
||||
|
||||
console.print(Panel(
|
||||
text,
|
||||
title="🤔 Command Suggestion",
|
||||
border_style="yellow",
|
||||
expand=False
|
||||
))
|
||||
console.print(
|
||||
Panel(text, title="🤔 Command Suggestion", border_style="yellow", expand=False)
|
||||
)
|
||||
|
||||
|
||||
def display_workflow_suggestion(original: str, suggestion: str):
|
||||
@@ -234,14 +236,13 @@ def display_workflow_suggestion(original: str, suggestion: str):
|
||||
text.append(f"'{suggestion}'", style="bold green")
|
||||
text.append("?\n\n")
|
||||
|
||||
text.append("💡 Use 'fuzzforge workflows' to see all available workflows", style="dim")
|
||||
text.append(
|
||||
"💡 Use 'fuzzforge workflows' to see all available workflows", style="dim"
|
||||
)
|
||||
|
||||
console.print(Panel(
|
||||
text,
|
||||
title="🔧 Workflow Suggestion",
|
||||
border_style="yellow",
|
||||
expand=False
|
||||
))
|
||||
console.print(
|
||||
Panel(text, title="🔧 Workflow Suggestion", border_style="yellow", expand=False)
|
||||
)
|
||||
|
||||
|
||||
def display_parameter_suggestion(original: str, suggestion: str):
|
||||
@@ -257,12 +258,9 @@ def display_parameter_suggestion(original: str, suggestion: str):
|
||||
|
||||
text.append("💡 Use '--help' to see all available parameters", style="dim")
|
||||
|
||||
console.print(Panel(
|
||||
text,
|
||||
title="⚙️ Parameter Suggestion",
|
||||
border_style="yellow",
|
||||
expand=False
|
||||
))
|
||||
console.print(
|
||||
Panel(text, title="⚙️ Parameter Suggestion", border_style="yellow", expand=False)
|
||||
)
|
||||
|
||||
|
||||
def enhanced_command_not_found_handler(command_parts: List[str]):
|
||||
@@ -306,4 +304,4 @@ def enhanced_parameter_not_found_handler(parameter_name: str):
|
||||
|
||||
|
||||
# Global fuzzy matcher instance
|
||||
fuzzy_matcher = FuzzyMatcher()
|
||||
fuzzy_matcher = FuzzyMatcher()
|
||||
|
||||
Reference in New Issue
Block a user