moriarty-project 0.1.6__py3-none-any.whl → 0.1.8__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.
moriarty/__init__.py CHANGED
@@ -1,5 +1,4 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  __all__ = ["__version__"]
4
-
5
- __version__ = "0.1.0"
4
+ __version__ = "0.1.8"
moriarty/cli/app.py CHANGED
@@ -24,9 +24,23 @@ app = typer.Typer(
24
24
  )
25
25
 
26
26
 
27
+ def version_callback(value: bool):
28
+ if value:
29
+ from .. import __version__
30
+ console.print(f"Moriarty OSINT Toolkit v{__version__}")
31
+ raise typer.Exit()
32
+
27
33
  @app.callback()
28
34
  def main(
29
35
  ctx: typer.Context,
36
+ version: bool = typer.Option(
37
+ None,
38
+ "--version",
39
+ "-v",
40
+ callback=version_callback,
41
+ is_eager=True,
42
+ help="Show version and exit.",
43
+ ),
30
44
  concurrency: int = typer.Option(50, min=1, help="Maximum concurrent tasks."),
31
45
  timeout: float = typer.Option(8.0, min=0.1, help="Per-request timeout in seconds."),
32
46
  proxy: str | None = typer.Option(None, help="HTTP/SOCKS proxy URI."),
@@ -46,7 +60,7 @@ def main(
46
60
  ),
47
61
  output: str | None = typer.Option(None, help="Path to export artifacts."),
48
62
  redact: bool = typer.Option(True, "--redact/--no-redact", help="Redact PII in output."),
49
- verbose: bool = typer.Option(False, help="Enable verbose logging."),
63
+ verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable verbose logging."),
50
64
  quiet: bool = typer.Option(False, help="Suppress non-critical output."),
51
65
  professional_mode: bool = typer.Option(False, help="Enable professional mode safeguards."),
52
66
  seed: int | None = typer.Option(None, help="Deterministic seed for planners."),
@@ -117,4 +131,86 @@ def main() -> None: # Console script compatibility
117
131
  main_entry()
118
132
 
119
133
 
120
- __all__ = ["app", "main"]
134
+ def check_pipx_installed() -> bool:
135
+ """Verifica se o pipx está instalado."""
136
+ try:
137
+ import subprocess
138
+ subprocess.run(["pipx", "--version"], capture_output=True, check=True)
139
+ return True
140
+ except (subprocess.SubprocessError, FileNotFoundError):
141
+ return False
142
+
143
+
144
+ def self_update():
145
+ """Atualiza o Moriarty para a versão mais recente."""
146
+ import subprocess
147
+ import sys
148
+ from rich.console import Console
149
+ from rich.panel import Panel
150
+ from rich import print
151
+
152
+ console = Console()
153
+
154
+ try:
155
+ # Verifica se está instalado com pipx
156
+ if check_pipx_installed():
157
+ console.print("🔄 Atualizando via pipx...", style="bold blue")
158
+ result = subprocess.run(
159
+ ["pipx", "install", "--upgrade", "moriarty-project"],
160
+ capture_output=True,
161
+ text=True
162
+ )
163
+ else:
164
+ console.print("🔄 Atualizando via pip...", style="bold blue")
165
+ result = subprocess.run(
166
+ [sys.executable, "-m", "pip", "install", "--upgrade", "moriarty-project"],
167
+ capture_output=True,
168
+ text=True
169
+ )
170
+
171
+ if result.returncode == 0:
172
+ console.print("✅ [bold green]Atualização concluída com sucesso![/]")
173
+ if result.stdout:
174
+ console.print(Panel(
175
+ result.stdout.strip(),
176
+ title="[bold]Saída do comando[/]",
177
+ border_style="green"
178
+ ))
179
+ console.print("\nReinicie o terminal para aplicar as alterações.", style="yellow")
180
+ else:
181
+ console.print("❌ [bold red]Erro durante a atualização:[/]")
182
+ if result.stderr:
183
+ console.print(Panel(
184
+ result.stderr.strip(),
185
+ title="[bold]Mensagem de erro[/]",
186
+ border_style="red"
187
+ ))
188
+ console.print("\nTente executar manualmente:")
189
+ if check_pipx_installed():
190
+ console.print(" [cyan]pipx install --upgrade moriarty-project[/]")
191
+ else:
192
+ console.print(f" [cyan]{sys.executable} -m pip install --upgrade moriarty-project[/]")
193
+ except Exception as e:
194
+ console.print(f"❌ [bold red]Erro inesperado:[/] {str(e)}")
195
+ console.print("\nTente atualizar manualmente usando:")
196
+ if check_pipx_installed():
197
+ console.print(" [cyan]pipx install --upgrade moriarty-project[/]")
198
+ else:
199
+ console.print(f" [cyan]{sys.executable} -m pip install --upgrade moriarty-project[/]")
200
+
201
+
202
+ # Adiciona o comando self-update
203
+ @app.command("self-update", help="Atualiza o Moriarty para a versão mais recente.")
204
+ def self_update_command():
205
+ """Atualiza o Moriarty para a versão mais recente."""
206
+ self_update()
207
+
208
+
209
+ # Adiciona um alias mais curto
210
+ @app.command("update", hidden=True)
211
+ def update_alias():
212
+ """Alias para 'self-update'."""
213
+ self_update()
214
+
215
+
216
+ __all__ = ["app", "main", "self_update"]
@@ -4,7 +4,7 @@ from typing import Optional
4
4
 
5
5
  import typer
6
6
 
7
- from moriarty.modules.port_scanner import PROFILES
7
+ from moriarty.modules.port_scanner import PortScanner, PROFILES
8
8
  from moriarty.modules.passive_recon import PassiveRecon
9
9
  from rich.console import Console
10
10
 
@@ -25,6 +25,7 @@ def scan_full(
25
25
  threads: int = typer.Option(10, "--threads", "-t", help="Threads concorrentes"),
26
26
  timeout: int = typer.Option(30, "--timeout", help="Timeout em segundos"),
27
27
  output: str = typer.Option(None, "--output", "-o", help="Arquivo de saída"),
28
+ verbose: bool = typer.Option(False, "--verbose", help="Ativar saída detalhada"),
28
29
  ):
29
30
  """
30
31
  🔍 Scan completo de domínio/IP com 14 módulos.
@@ -49,6 +50,7 @@ def scan_full(
49
50
  stealth_level=stealth,
50
51
  threads=threads,
51
52
  timeout=timeout,
53
+ verbose=verbose,
52
54
  )
53
55
 
54
56
  asyncio.run(scanner.run())
@@ -284,6 +286,7 @@ def port_scan(
284
286
  scan_type: str = typer.Option("syn", "--type", "-t", help="Tipo: syn, tcp, udp"),
285
287
  stealth: int = typer.Option(0, "--stealth", "-s", help="Stealth level (0-4)"),
286
288
  output: str = typer.Option(None, "--output", "-o", help="Arquivo de saída"),
289
+ verbose: bool = typer.Option(False, "--verbose", help="Ativar saída detalhada"),
287
290
  ):
288
291
  """
289
292
  🔌 Scan avançado de portas.
@@ -496,39 +499,133 @@ def passive_recon(
496
499
 
497
500
  @app.command("ports")
498
501
  def port_scan(
499
- domain: str = typer.Argument(..., help="Alvo para o port scan"),
500
- profile: str = typer.Option("quick", "--profile", "-p", help=f"Perfil ({', '.join(PROFILES.keys())})"),
501
- concurrency: int = typer.Option(200, "--concurrency", help="Conexões simultâneas"),
502
- timeout: float = typer.Option(1.5, "--timeout", help="Timeout por porta"),
503
- output: Optional[str] = typer.Option(None, "--output", "-o", help="Arquivo de saída JSON"),
502
+ target: str = typer.Argument(..., help="Domínio ou IP para escanear"),
503
+ profile: str = typer.Option(
504
+ "quick",
505
+ "--profile",
506
+ "-p",
507
+ help=f"Perfil de varredura: {', '.join(PROFILES.keys())}"
508
+ ),
509
+ stealth: int = typer.Option(
510
+ 0,
511
+ "--stealth",
512
+ "-s",
513
+ min=0,
514
+ max=5,
515
+ help="Nível de stealth (0-5, maior = mais lento e discreto)",
516
+ ),
517
+ concurrency: int = typer.Option(
518
+ 200,
519
+ "--concurrency",
520
+ "-c",
521
+ help="Número de conexões simultâneas",
522
+ min=1,
523
+ max=1000,
524
+ ),
525
+ timeout: float = typer.Option(
526
+ 2.0,
527
+ "--timeout",
528
+ "-t",
529
+ help="Timeout por porta em segundos",
530
+ min=0.5,
531
+ max=30.0,
532
+ ),
533
+ output: Optional[str] = typer.Option(
534
+ None,
535
+ "--output",
536
+ "-o",
537
+ help="Arquivo de saída (formato: .json, .txt, .md)"
538
+ ),
539
+ format: str = typer.Option(
540
+ "text",
541
+ "--format",
542
+ "-f",
543
+ help="Formato de saída: text, json, markdown",
544
+ ),
545
+ resolve_services: bool = typer.Option(
546
+ True,
547
+ "--resolve/--no-resolve",
548
+ help="Tentar identificar serviços nas portas abertas",
549
+ ),
550
+ check_vulns: bool = typer.Option(
551
+ True,
552
+ "--vulns/--no-vulns",
553
+ help="Verificar vulnerabilidades conhecidas",
554
+ ),
504
555
  ):
505
- """Executa port scan assíncrono com banners."""
556
+ """
557
+ 🔍 Varredura avançada de portas com detecção de serviços e vulnerabilidades.
558
+
559
+ Exemplos:
560
+ moriarty domain ports example.com
561
+ moriarty domain ports 192.168.1.1 --profile full --stealth 3
562
+ moriarty domain ports target.com -o resultado.json --format json
563
+ """
506
564
  import asyncio
507
565
  import json
566
+ from pathlib import Path
567
+ from moriarty.modules.port_scanner import format_scan_results
568
+
569
+ # Validações
570
+ if profile not in PROFILES:
571
+ console.print(f"[red]Erro:[/red] Perfil inválido. Use um destes: {', '.join(PROFILES.keys())}")
572
+ raise typer.Exit(1)
573
+
574
+ if output:
575
+ output = Path(output)
576
+ if output.suffix not in (".json", ".txt", ".md"):
577
+ console.print("[yellow]Aviso:[/yellow] Extensão de arquivo não suportada. Usando .json")
578
+ output = output.with_suffix(".json")
508
579
 
509
580
  async def _run():
510
581
  scanner = PortScanner(
511
- target=domain,
582
+ target=target,
512
583
  profile=profile,
513
584
  concurrency=concurrency,
514
585
  timeout=timeout,
586
+ stealth_level=stealth,
587
+ resolve_services=resolve_services,
588
+ check_vulns=check_vulns,
515
589
  )
516
590
  return await scanner.scan()
517
591
 
518
- results = asyncio.run(_run())
519
- if not results:
520
- console.print("[yellow]Nenhuma porta aberta detectada[/yellow]")
521
- else:
522
- table_data = [
523
- {"port": r.port, "status": r.status, "banner": r.banner or "-"}
524
- for r in results
525
- ]
526
- console.print_json(data=table_data)
527
-
528
- if output:
529
- json_payload = [r.__dict__ for r in results]
530
- with open(output, "w", encoding="utf-8") as handle:
531
- json.dump(json_payload, handle, indent=2, ensure_ascii=False)
592
+ # Executa o scanner
593
+ console.print(f"[bold]🔍 Iniciando varredura em:[/bold] {target}")
594
+ console.print(f"📊 Perfil: {profile} (portas: {len(PROFILES[profile])})")
595
+ console.print(f"🕵️ Nível de stealth: {stealth}")
596
+
597
+ try:
598
+ results = asyncio.run(_run())
599
+
600
+ if not results:
601
+ console.print("[yellow]⚠️ Nenhuma porta aberta detectada[/yellow]")
602
+ return
603
+
604
+ # Formata a saída
605
+ if format.lower() == "json":
606
+ output_text = json.dumps([r.to_dict() for r in results], indent=2)
607
+ if not output:
608
+ console.print_json(data=json.loads(output_text))
609
+ else:
610
+ output_text = format_scan_results(results, output_format=format)
611
+ if not output:
612
+ console.print(output_text)
613
+
614
+ # Salva em arquivo se especificado
615
+ if output:
616
+ output.parent.mkdir(parents=True, exist_ok=True)
617
+ with open(output, "w", encoding="utf-8") as f:
618
+ if output.suffix == ".json":
619
+ json.dump([r.to_dict() for r in results], f, indent=2, ensure_ascii=False)
620
+ else:
621
+ f.write(output_text)
622
+ console.print(f"\n[green]✅ Resultados salvos em:[/green] {output.absolute()}")
623
+
624
+ except Exception as e:
625
+ console.print(f"[red]❌ Erro durante a varredura:[/red] {str(e)}")
626
+ if "object NoneType can't be used in 'await' expression" in str(e):
627
+ console.print("[yellow]Dica:[/yellow] Tente aumentar o timeout com --timeout 5.0")
628
+ raise typer.Exit(1)
532
629
  console.print(f"[green]✓ Resultado salvo em[/green] {output}")
533
630
 
534
631
 
@@ -76,17 +76,23 @@ class DomainScanner:
76
76
  stealth_level: int = 0,
77
77
  threads: int = 10,
78
78
  timeout: int = 30,
79
+ verbose: bool = False,
79
80
  ):
80
81
  self.target = target
81
82
  self.modules = modules or self.DEFAULT_MODULES
82
83
  self.stealth_level = stealth_level
83
84
  self.threads = threads
84
85
  self.timeout = timeout
86
+ self.verbose = verbose
85
87
  self.result = ScanResult(target=target)
86
88
  self.stealth = None
89
+
90
+ # Configura o nível de log com base no modo verbose
91
+ log_level = logging.DEBUG if verbose else logging.INFO
92
+ logging.basicConfig(level=log_level)
93
+
87
94
  if self.stealth_level > 0:
88
95
  from moriarty.modules.stealth_mode import StealthMode
89
-
90
96
  self.stealth = StealthMode(level=self.stealth_level)
91
97
 
92
98
  self.tech_profile: Optional[Dict[str, Any]] = None