Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 133 additions & 13 deletions macaron
Original file line number Diff line number Diff line change
Expand Up @@ -2081,38 +2081,158 @@ def cmd_install(args):

console.print("[cyan]Installing reconnaissance tools...[/]") if console else print("Installing...")

# Install Go tools
# Go tools (ProjectDiscovery + community)
go_tools = [
# Subdomain enumeration
"github.com/projectdiscovery/subfinder/v2/cmd/subfinder@latest",
"github.com/projectdiscovery/httpx/cmd/httpx@latest",
"github.com/tomnomnom/assetfinder@latest",
"github.com/gwen001/github-subdomains@latest",
# DNS
"github.com/projectdiscovery/dnsx/cmd/dnsx@latest",
"github.com/projectdiscovery/shuffledns/cmd/shuffledns@latest",
"github.com/d3mondev/puredns/v2@latest",
"github.com/hakluke/hakrevdns@latest",
# ASN/IP
"github.com/projectdiscovery/asnmap/cmd/asnmap@latest",
"github.com/projectdiscovery/mapcidr/cmd/mapcidr@latest",
# Ports
"github.com/projectdiscovery/naabu/v2/cmd/naabu@latest",
"github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest",
"github.com/projectdiscovery/katana/cmd/katana@latest",
"github.com/tomnomnom/assetfinder@latest",
# HTTP
"github.com/projectdiscovery/httpx/cmd/httpx@latest",
"github.com/tomnomnom/httprobe@latest",
# URLs
"github.com/lc/gau/v2/cmd/gau@latest",
"github.com/tomnomnom/waybackurls@latest",
"github.com/sensepost/gowitness@latest",
"github.com/ffuf/ffuf/v2@latest",
"github.com/projectdiscovery/katana/cmd/katana@latest",
"github.com/hakluke/hakrawler@latest",
"github.com/lc/gau/v2/cmd/gau@latest",
"github.com/jaeles-project/gospider@latest",
# JS
"github.com/003random/getJS@latest",
"github.com/lc/subjs@latest",
# Content
"github.com/ffuf/ffuf/v2@latest",
# Takeover
"github.com/haccer/subjack@latest",
# Screenshots
"github.com/sensepost/gowitness@latest",
# Nuclei (for takeover templates)
"github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest",
]

# Python tools
pip_tools = [
"dnsgen",
"altdns",
"arjun",
"paramspider",
"linkfinder",
"s3scanner",
]

# APT packages (Debian/Ubuntu/Kali)
apt_tools = [
"amass",
"findomain",
"massdns",
"nmap",
"masscan",
"whatweb",
"feroxbuster",
"theharvester",
"eyewitness",
"dnsrecon",
"jq",
"proxychains4",
]

os.environ["GOPATH"] = str(Path.home() / "go")
os.environ["PATH"] = os.environ["PATH"] + ":" + str(Path.home() / "go" / "bin")

# Install Go tools
console.print("\n[bold cyan]Installing Go tools...[/]") if console else None
for tool in go_tools:
name = tool.split('/')[-1].split('@')[0]
console.print(f" Installing {name}...") if console else print(f"Installing {name}")
subprocess.run(["go", "install", tool], capture_output=True)
console.print(f" [dim]go install[/] {name}") if console else print(f"Installing {name}")
result = subprocess.run(["go", "install", tool], capture_output=True, text=True)
if result.returncode != 0:
console.print(f" [yellow]⚠ Failed[/]") if console else None

# Copy to /usr/local/bin
# Copy Go binaries to /usr/local/bin
go_bin = Path.home() / "go" / "bin"
if go_bin.exists():
for f in go_bin.iterdir():
shutil.copy(str(f), "/usr/local/bin/")
try:
shutil.copy(str(f), "/usr/local/bin/")
except Exception:
pass

# Install Python tools
console.print("\n[bold cyan]Installing Python tools...[/]") if console else None
for tool in pip_tools:
console.print(f" [dim]pip install[/] {tool}") if console else print(f"Installing {tool}")
subprocess.run(["pip3", "install", tool], capture_output=True)
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pip3 installation command for Python tools lacks error handling. If pip3 is not available or the installation fails, the command will silently fail without informing the user. Consider capturing the return code and displaying a warning similar to the Go tools installation pattern.

Suggested change
subprocess.run(["pip3", "install", tool], capture_output=True)
try:
result = subprocess.run(["pip3", "install", tool], capture_output=True, text=True)
if result.returncode != 0:
console.print(f" [yellow]⚠ Failed[/]") if console else None
except FileNotFoundError:
if console:
console.print(" [yellow]⚠ pip3 not found; skipping Python tools installation[/]")
else:
print("Warning: pip3 not found; skipping Python tools installation")
break

Copilot uses AI. Check for mistakes.

# Install APT packages
console.print("\n[bold cyan]Installing system packages...[/]") if console else None
subprocess.run(["apt-get", "update", "-qq"], capture_output=True)
for tool in apt_tools:
console.print(f" [dim]apt install[/] {tool}") if console else print(f"Installing {tool}")
subprocess.run(["apt-get", "install", "-y", "-qq", tool], capture_output=True)
Comment on lines +2177 to +2180
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The APT update and install commands are run without error handling and without checking if the system uses APT (Debian/Ubuntu/Kali). This will fail silently on non-Debian systems like Fedora, macOS, or Arch Linux. Consider checking if apt-get exists before running these commands, or adding error handling with user feedback.

Suggested change
subprocess.run(["apt-get", "update", "-qq"], capture_output=True)
for tool in apt_tools:
console.print(f" [dim]apt install[/] {tool}") if console else print(f"Installing {tool}")
subprocess.run(["apt-get", "install", "-y", "-qq", tool], capture_output=True)
apt_get_path = shutil.which("apt-get")
if not apt_get_path:
msg = "APT (apt-get) not found on this system. Skipping installation of system packages."
console.print(f"[yellow]{msg}[/]") if console else print(msg)
else:
update_result = subprocess.run([apt_get_path, "update", "-qq"], capture_output=True, text=True)
if update_result.returncode != 0:
msg = "Failed to run 'apt-get update'. Skipping installation of system packages."
console.print(f"[yellow]{msg}[/]") if console else print(msg)
else:
for tool in apt_tools:
console.print(f" [dim]apt install[/] {tool}") if console else print(f"Installing {tool}")
install_result = subprocess.run(
[apt_get_path, "install", "-y", "-qq", tool],
capture_output=True,
text=True
)
if install_result.returncode != 0:
warn_msg = f"Failed to install '{tool}' via APT."
console.print(f"[yellow]{warn_msg}[/]") if console else print(warn_msg)

Copilot uses AI. Check for mistakes.
Comment on lines +2177 to +2180
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The APT package installation commands require root privileges but no check is performed and no error is reported if the command fails due to insufficient permissions. Users will not know why the installation failed. Consider adding privilege checks or error handling to inform users they need to run with sudo.

Suggested change
subprocess.run(["apt-get", "update", "-qq"], capture_output=True)
for tool in apt_tools:
console.print(f" [dim]apt install[/] {tool}") if console else print(f"Installing {tool}")
subprocess.run(["apt-get", "install", "-y", "-qq", tool], capture_output=True)
if hasattr(os, "geteuid") and os.geteuid() != 0:
msg = (
"Skipping APT package installation because root privileges are required. "
"Please re-run this command with sudo or as root to install system packages."
)
console.print(f"[yellow]{msg}[/]") if console else print(msg)
else:
update_result = subprocess.run(
["apt-get", "update", "-qq"],
capture_output=True,
text=True,
)
if update_result.returncode != 0:
warn_msg = (
f"apt-get update failed with exit code {update_result.returncode}. "
"You may need to run this command with sudo or as root."
)
console.print(f"[yellow]{warn_msg}[/]") if console else print(warn_msg)
for tool in apt_tools:
console.print(f" [dim]apt install[/] {tool}") if console else print(f"Installing {tool}")
install_result = subprocess.run(
["apt-get", "install", "-y", "-qq", tool],
capture_output=True,
text=True,
)
if install_result.returncode != 0:
warn_msg = (
f"Failed to install {tool} via apt-get (exit code {install_result.returncode}). "
"You may need to run this command with sudo or as root."
)
console.print(f" [yellow]⚠ {warn_msg}[/]") if console else print(warn_msg)

Copilot uses AI. Check for mistakes.

# Install additional tools that need special handling
console.print("\n[bold cyan]Installing additional tools...[/]") if console else None

# Kiterunner
console.print(" [dim]Installing[/] kiterunner") if console else None
subprocess.run([
"bash", "-c",
"cd /tmp && git clone https://github.com/assetnote/kiterunner.git 2>/dev/null && cd kiterunner && make build && cp dist/kr /usr/local/bin/ 2>/dev/null"
Comment on lines +2187 to +2189
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This git clone + make build pipeline pulls and executes arbitrary code from the HEAD of a third-party GitHub repo with no pinning or integrity verification. If the repo or its build scripts are compromised, running this installer gives an attacker code execution with the installer’s privileges and lets them drop a binary into /usr/local/bin. To reduce this supply-chain risk, pin to a specific commit or signed release and verify it (or vendor a known-good artifact) instead of building whatever is on the default branch at install time.

Copilot uses AI. Check for mistakes.
], capture_output=True)
Comment on lines +2187 to +2190
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The kiterunner installation uses a complex bash command with git clone and make build that could fail at multiple points, but all failures are silently ignored. If git, make, or the repository is unavailable, users won't know the installation failed. Consider adding error checking or at least capturing and reporting the return code.

Suggested change
subprocess.run([
"bash", "-c",
"cd /tmp && git clone https://github.com/assetnote/kiterunner.git 2>/dev/null && cd kiterunner && make build && cp dist/kr /usr/local/bin/ 2>/dev/null"
], capture_output=True)
kr_result = subprocess.run(
[
"bash",
"-c",
"cd /tmp && git clone https://github.com/assetnote/kiterunner.git 2>/dev/null && cd kiterunner && make build && cp dist/kr /usr/local/bin/ 2>/dev/null",
],
capture_output=True,
text=True,
)
if kr_result.returncode != 0:
err_msg = f" Failed to install kiterunner (exit code {kr_result.returncode})."
if console:
console.print(f"[red]{err_msg}[/]")
if kr_result.stderr:
console.print(f"[red]{kr_result.stderr}[/]")
else:
print(err_msg, file=sys.stderr)
if kr_result.stderr:
print(kr_result.stderr, file=sys.stderr)

Copilot uses AI. Check for mistakes.

# x8 (Rust)
console.print(" [dim]Installing[/] x8") if console else None
subprocess.run(["cargo", "install", "x8"], capture_output=True)
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The x8 installation via cargo has no error handling and no check if cargo (Rust toolchain) is installed. If cargo is not available, the installation will fail silently without informing the user. Consider checking if cargo exists or adding error handling similar to the Go tools pattern.

Suggested change
subprocess.run(["cargo", "install", "x8"], capture_output=True)
cargo_path = shutil.which("cargo")
if not cargo_path:
msg = "cargo (Rust toolchain) not found; skipping x8 installation. Please install Rust to use x8."
if console:
console.print(f"[yellow]{msg}[/]")
else:
print(msg, file=sys.stderr)
else:
result = subprocess.run([cargo_path, "install", "x8"], capture_output=True, text=True)
if result.returncode != 0:
error_output = (result.stderr or result.stdout or "").strip()
msg = "Failed to install x8 via cargo."
if error_output:
msg = f"{msg} Details: {error_output}"
if console:
console.print(f"[red]{msg}[/]")
else:
print(msg, file=sys.stderr)

Copilot uses AI. Check for mistakes.

# favfreak
console.print(" [dim]Installing[/] favfreak") if console else None
subprocess.run([
"bash", "-c",
"pip3 install mmh3 && cd /tmp && git clone https://github.com/devanshbatham/FavFreak.git 2>/dev/null && cp FavFreak/favfreak.py /usr/local/bin/favfreak && chmod +x /usr/local/bin/favfreak 2>/dev/null"
Comment on lines +2198 to +2200
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This command first runs pip3 install mmh3 and then clones https://github.com/devanshbatham/FavFreak.git from a mutable HEAD and copies its script into /usr/local/bin without any version pinning or integrity checks. A compromised PyPI package or GitHub repo here would immediately execute attacker-controlled code during installation and persist it as a system-wide tool. Prefer pinning to specific versions/commits with known hashes or trusted packages you control rather than installing arbitrary latest code each time.

Copilot uses AI. Check for mistakes.
], capture_output=True)
Comment on lines +2198 to +2201
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The favfreak installation command chains multiple operations (pip3 install mmh3, git clone, cp, chmod) in a bash command where failures are silently ignored due to stderr redirection and no error checking. Users won't know if any step fails. Consider breaking this into separate commands with proper error handling.

Suggested change
subprocess.run([
"bash", "-c",
"pip3 install mmh3 && cd /tmp && git clone https://github.com/devanshbatham/FavFreak.git 2>/dev/null && cp FavFreak/favfreak.py /usr/local/bin/favfreak && chmod +x /usr/local/bin/favfreak 2>/dev/null"
], capture_output=True)
# Install mmh3 dependency
favfreak_mm_result = subprocess.run(
["pip3", "install", "mmh3"],
capture_output=True,
text=True,
)
if favfreak_mm_result.returncode != 0:
msg = " Failed to install mmh3 for favfreak."
if console:
console.print(f"[red]{msg}[/]")
else:
print(msg, file=sys.stderr)
# Clone FavFreak repository
favfreak_clone_result = subprocess.run(
["git", "clone", "https://github.com/devanshbatham/FavFreak.git"],
cwd="/tmp",
capture_output=True,
text=True,
)
if favfreak_clone_result.returncode != 0:
msg = " Failed to clone FavFreak repository (it may already exist)."
if console:
console.print(f"[yellow]{msg}[/]")
else:
print(msg, file=sys.stderr)
# Copy favfreak script to /usr/local/bin and make it executable
favfreak_src = "/tmp/FavFreak/favfreak.py"
favfreak_dst = "/usr/local/bin/favfreak"
try:
shutil.copyfile(favfreak_src, favfreak_dst)
os.chmod(favfreak_dst, 0o755)
except OSError as e:
msg = f" Failed to install favfreak script: {e}"
if console:
console.print(f"[red]{msg}[/]")
else:
print(msg, file=sys.stderr)

Copilot uses AI. Check for mistakes.

# webanalyze
console.print(" [dim]Installing[/] webanalyze") if console else None
subprocess.run(["go", "install", "github.com/rverton/webanalyze/cmd/webanalyze@latest"], capture_output=True)

# cloud_enum
console.print(" [dim]Installing[/] cloud_enum") if console else None
subprocess.run([
"bash", "-c",
"cd /tmp && git clone https://github.com/initstring/cloud_enum.git 2>/dev/null && cd cloud_enum && pip3 install -r requirements.txt && ln -sf $(pwd)/cloud_enum.py /usr/local/bin/cloud_enum 2>/dev/null"
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cloud_enum installation uses a symlink to /usr/local/bin which requires root privileges but has no error handling. The 2>/dev/null suppresses errors, so users won't know if the symlink creation failed due to insufficient permissions. Consider adding error handling or privilege checks.

Suggested change
"cd /tmp && git clone https://github.com/initstring/cloud_enum.git 2>/dev/null && cd cloud_enum && pip3 install -r requirements.txt && ln -sf $(pwd)/cloud_enum.py /usr/local/bin/cloud_enum 2>/dev/null"
"cd /tmp && git clone https://github.com/initstring/cloud_enum.git 2>/dev/null && cd cloud_enum && pip3 install -r requirements.txt && if [ -w /usr/local/bin ] || [ \"$(id -u)\" -eq 0 ]; then ln -sf \"$(pwd)/cloud_enum.py\" /usr/local/bin/cloud_enum; else echo 'Warning: insufficient permissions to create /usr/local/bin/cloud_enum symlink. Please run macaron with elevated privileges or create the symlink manually.' >&2; fi"

Copilot uses AI. Check for mistakes.
Comment on lines +2209 to +2211
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you clone https://github.com/initstring/cloud_enum.git at HEAD and run pip3 install -r requirements.txt, allowing that repo to specify arbitrary dependencies and install scripts with no integrity verification. If the repo or its requirements.txt is tampered with, this installer grants an attacker code execution and lets them drop or modify tools under /usr/local/bin. Consider pinning to a specific commit or tagged release and using a locked requirements file with hashes so you do not execute or install arbitrary updated code on each run.

Copilot uses AI. Check for mistakes.
], capture_output=True)
Comment on lines +2187 to +2212
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The kiterunner, favfreak, and cloud_enum installations use /tmp directory for cloning repositories, but there's no cleanup after installation. This leaves repository data in /tmp which could accumulate over multiple runs. Consider cleaning up the temporary directories after installation or using a more appropriate temporary directory with automatic cleanup.

Copilot uses AI. Check for mistakes.
Comment on lines +2196 to +2212
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Operations that write to /usr/local/bin (copying Go binaries, installing kiterunner, favfreak, cloud_enum symlink) require root privileges. While exceptions are caught for Go binaries, other operations silently fail. Consider checking for necessary privileges at the start of the function or providing clear guidance that the command needs to be run with sudo.

Suggested change
# favfreak
console.print(" [dim]Installing[/] favfreak") if console else None
subprocess.run([
"bash", "-c",
"pip3 install mmh3 && cd /tmp && git clone https://github.com/devanshbatham/FavFreak.git 2>/dev/null && cp FavFreak/favfreak.py /usr/local/bin/favfreak && chmod +x /usr/local/bin/favfreak 2>/dev/null"
], capture_output=True)
# webanalyze
console.print(" [dim]Installing[/] webanalyze") if console else None
subprocess.run(["go", "install", "github.com/rverton/webanalyze/cmd/webanalyze@latest"], capture_output=True)
# cloud_enum
console.print(" [dim]Installing[/] cloud_enum") if console else None
subprocess.run([
"bash", "-c",
"cd /tmp && git clone https://github.com/initstring/cloud_enum.git 2>/dev/null && cd cloud_enum && pip3 install -r requirements.txt && ln -sf $(pwd)/cloud_enum.py /usr/local/bin/cloud_enum 2>/dev/null"
], capture_output=True)
# Check permissions for installing into /usr/local/bin
usr_local_bin_path = "/usr/local/bin"
usr_local_bin_writable = os.access(usr_local_bin_path, os.W_OK)
if not usr_local_bin_writable:
message = (
f"[yellow]Warning:[/] No write permission to {usr_local_bin_path}. "
"Skipping installation of tools that require copying or linking files there. "
"Try re-running this command with sudo if you want those tools installed."
)
if console:
console.print(message)
else:
print(message)
# favfreak
if usr_local_bin_writable:
console.print(" [dim]Installing[/] favfreak") if console else None
subprocess.run([
"bash", "-c",
"pip3 install mmh3 && cd /tmp && git clone https://github.com/devanshbatham/FavFreak.git 2>/dev/null && cp FavFreak/favfreak.py /usr/local/bin/favfreak && chmod +x /usr/local/bin/favfreak 2>/dev/null"
], capture_output=True)
# webanalyze
console.print(" [dim]Installing[/] webanalyze") if console else None
subprocess.run(["go", "install", "github.com/rverton/webanalyze/cmd/webanalyze@latest"], capture_output=True)
# cloud_enum
if usr_local_bin_writable:
console.print(" [dim]Installing[/] cloud_enum") if console else None
subprocess.run([
"bash", "-c",
"cd /tmp && git clone https://github.com/initstring/cloud_enum.git 2>/dev/null && cd cloud_enum && pip3 install -r requirements.txt && ln -sf $(pwd)/cloud_enum.py /usr/local/bin/cloud_enum 2>/dev/null"
], capture_output=True)

Copilot uses AI. Check for mistakes.
Comment on lines +2187 to +2212
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multiple installation commands (kiterunner, favfreak, cloud_enum) use complex bash one-liners that are difficult to read, maintain, and debug. Consider breaking these into separate subprocess calls or creating helper functions to improve code maintainability and error handling capabilities.

Copilot uses AI. Check for mistakes.
Comment on lines +2189 to +2212
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The stderr redirection 2>/dev/null in several bash commands suppresses all error output, making debugging installation failures extremely difficult. Users and developers won't be able to see what went wrong when installations fail. Consider removing these redirections or capturing stderr for logging purposes.

Copilot uses AI. Check for mistakes.

# emailfinder
console.print(" [dim]Installing[/] emailfinder") if console else None
subprocess.run(["pip3", "install", "emailfinder"], capture_output=True)

# Update nuclei templates
console.print("\n[bold cyan]Updating nuclei templates...[/]") if console else None
subprocess.run(["nuclei", "-update-templates"], capture_output=True)
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nuclei template update command has no error handling. If nuclei is not installed or the update fails, users won't be informed. Consider checking if nuclei exists first and adding error handling to inform users if the update fails.

Suggested change
subprocess.run(["nuclei", "-update-templates"], capture_output=True)
nuclei_path = shutil.which("nuclei")
if nuclei_path:
try:
result = subprocess.run(
[nuclei_path, "-update-templates"],
capture_output=True,
text=True
)
if result.returncode != 0:
msg = "Failed to update nuclei templates."
detail = (result.stderr or "").strip()
if console:
console.print(f"[red]{msg}[/]")
if detail:
console.print(f"[dim]{detail}[/]")
else:
print(msg, file=sys.stderr)
if detail:
print(detail, file=sys.stderr)
except Exception as e:
msg = f"Error while updating nuclei templates: {e}"
if console:
console.print(f"[red]{msg}[/]")
else:
print(msg, file=sys.stderr)
else:
msg = "nuclei is not installed or not found in PATH; skipping nuclei template update."
if console:
console.print(f"[yellow]{msg}[/]")
else:
print(msg, file=sys.stderr)

Copilot uses AI. Check for mistakes.

Comment on lines +2198 to +2221
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The installation process runs sequentially and could take a very long time to complete since it's installing 48+ tools. Consider implementing parallel installation where safe (e.g., Go tools could be installed concurrently) to improve installation performance and user experience.

Suggested change
subprocess.run([
"bash", "-c",
"pip3 install mmh3 && cd /tmp && git clone https://github.com/devanshbatham/FavFreak.git 2>/dev/null && cp FavFreak/favfreak.py /usr/local/bin/favfreak && chmod +x /usr/local/bin/favfreak 2>/dev/null"
], capture_output=True)
# webanalyze
console.print(" [dim]Installing[/] webanalyze") if console else None
subprocess.run(["go", "install", "github.com/rverton/webanalyze/cmd/webanalyze@latest"], capture_output=True)
# cloud_enum
console.print(" [dim]Installing[/] cloud_enum") if console else None
subprocess.run([
"bash", "-c",
"cd /tmp && git clone https://github.com/initstring/cloud_enum.git 2>/dev/null && cd cloud_enum && pip3 install -r requirements.txt && ln -sf $(pwd)/cloud_enum.py /usr/local/bin/cloud_enum 2>/dev/null"
], capture_output=True)
# emailfinder
console.print(" [dim]Installing[/] emailfinder") if console else None
subprocess.run(["pip3", "install", "emailfinder"], capture_output=True)
# Update nuclei templates
console.print("\n[bold cyan]Updating nuclei templates...[/]") if console else None
subprocess.run(["nuclei", "-update-templates"], capture_output=True)
# Prepare installation and update commands to run in parallel
favfreak_cmd = [
"bash", "-c",
"pip3 install mmh3 && cd /tmp && git clone https://github.com/devanshbatham/FavFreak.git 2>/dev/null && cp FavFreak/favfreak.py /usr/local/bin/favfreak && chmod +x /usr/local/bin/favfreak 2>/dev/null"
]
# webanalyze
console.print(" [dim]Installing[/] webanalyze") if console else None
webanalyze_cmd = ["go", "install", "github.com/rverton/webanalyze/cmd/webanalyze@latest"]
# cloud_enum
console.print(" [dim]Installing[/] cloud_enum") if console else None
cloud_enum_cmd = [
"bash", "-c",
"cd /tmp && git clone https://github.com/initstring/cloud_enum.git 2>/dev/null && cd cloud_enum && pip3 install -r requirements.txt && ln -sf $(pwd)/cloud_enum.py /usr/local/bin/cloud_enum 2>/dev/null"
]
# emailfinder
console.print(" [dim]Installing[/] emailfinder") if console else None
emailfinder_cmd = ["pip3", "install", "emailfinder"]
# Update nuclei templates
console.print("\n[bold cyan]Updating nuclei templates...[/]") if console else None
nuclei_cmd = ["nuclei", "-update-templates"]
install_commands = [
favfreak_cmd,
webanalyze_cmd,
cloud_enum_cmd,
emailfinder_cmd,
nuclei_cmd,
]
# Run the installation and update commands in parallel to improve performance
max_workers = min(4, len(install_commands))
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = [
executor.submit(subprocess.run, cmd, capture_output=True)
for cmd in install_commands
]
for future in as_completed(futures):
# Ensure all commands complete and surface any exceptions
future.result()

Copilot uses AI. Check for mistakes.
# Create resolvers file if missing
resolvers_file = Path.home() / ".macaron" / "config" / "resolvers.txt"
if not resolvers_file.exists():
console.print("\n[bold cyan]Creating resolvers file...[/]") if console else None
resolvers_file.parent.mkdir(parents=True, exist_ok=True)
resolvers = [
"8.8.8.8", "8.8.4.4", "1.1.1.1", "1.0.0.1",
"9.9.9.9", "149.112.112.112", "208.67.222.222", "208.67.220.220"
]
with open(resolvers_file, 'w') as f:
f.write('\n'.join(resolvers))

console.print("[green]✓ Installation complete![/]") if console else print("Done!")
console.print("\n[green]✓ Installation complete![/]") if console else print("Done!")
console.print("[dim]Run 'macaron -L' to check installed tools[/]") if console else None
return 0

def cmd_update(args):
Expand Down