Detect lookalike and IDN homograph phishing domains to protect against spoofing attacks
- Dual Detection Modes: Identifies both ASCII lookalike domains (paypai.com) and IDN/Punycode homograph attacks (амаzоn.com)
- CLI and API Server: Use as a command-line tool or run as an HTTP microservice for SOAR/security automation integration
- Auto-Detection: Automatically determines if a domain is ASCII or IDN and uses the appropriate lookup method
- Flexible Output: Export results as JSON, plain text, or CSV for easy parsing and analysis
- Structured Logging: Configurable logging with plain or JSON format, outputting to stdout, stderr, or file
- Production Ready: Built-in health check endpoint and OpenAPI documentation for easy integration
- Lightweight: Self-contained binary with no external dependencies required
- Smart URL Parsing: Strips schemes (
http://,https://,ftp://, etc), paths/queries/fragments and ports. Only the domain & TLD are analysed.
- ASCII detection requires the domain database (~150MB download on first use)
- IDN detection covers common homograph attacks but may not catch all Unicode spoofing variants
- Similarity scoring is heuristic-based and may produce false positives/negatives
- Does not verify if detected lookalike domains are actually malicious
# Linux example
# Download the latest release (or download using the releases page)
curl -LO https://github.com/slimpagey/spotspoof-cli/releases/latest/download/spotspoof-linux-amd64
# Make it executable
chmod +x spotspoof-linux-amd64
mv spotspoof-linux-amd64 spotspoof
# Run it
./spotspoof --helpDownload the latest release for your platform. The CLI auto-downloads the SQLite DB on first run if it’s missing.
| Platform | Download |
|---|---|
| Linux (x86_64) | spotspoof-linux-amd64 |
| macOS (Apple Silicon) | spotspoof-darwin-arm64 |
| Windows | spotspoof-windows-amd64.exe |
Or use this one-liner to download and install (Linux/macOS):
curl -fsSL https://raw.githubusercontent.com/slimpagey/spotspoof-cli/main/install.sh | bash# Homebrew
brew tap slimpagey/spotspoof
# Cargo
cargo install spotspoof-cliPrerequisites:
- Rust 1.83 or higher (Install Rust)
Build:
# Clone the repository
git clone https://github.com/slimpagey/spotspoof-cli.git
cd spotspoof-cli
# Build the release binary
cargo build --release
# The binary will be at target/release/spotspoof
./target/release/spotspoof --helpInstall system-wide:
cargo install --path .spotspoof [OPTIONS] <COMMAND>| Command | Description |
|---|---|
lookup |
Auto-detect ASCII vs IDN lookup |
ascii |
ASCII spoof lookup |
idn |
IDN lookup |
serve |
Run an HTTP server for lookups |
help |
Print help information |
| Option | Description | Default |
|---|---|---|
--log-format <FORMAT> |
Log output format (plain or json) |
plain |
--log-destination <DEST> |
Log destination (stdout, stderr, or file) |
stdout |
--log-file <PATH> |
Log file path (required when --log-destination=file) |
- |
-h, --help |
Print help information | - |
-V, --version |
Print version information | - |
Supported by: lookup, ascii, and idn commands
| Option | Description |
|---|---|
| (default) | JSON output |
-t, --text |
Plain text output |
--csv |
CSV output |
-o, --outfile <PATH> |
Write output to a file |
- The SQLite DB (~230MB) is auto-downloaded from GitHub releases on first ASCII lookup if missing
- Database location:
./spotspoof.sqlite(current directory). - Use
--no-dbonlookup,ascii, orserveto skip DB usage (ASCII results will be empty)
Start the HTTP server:
spotspoof serve --host 127.0.0.1 --port 8080 --db spotspoof.sqliteAvailable Routes:
GET /- API informationGET /healthz- Health check endpointPOST /lookup- Auto-detect and lookup domainPOST /ascii- ASCII spoof lookupPOST /idn- IDN/Punycode lookupGET /docs- API documentation
Example 1: Lookup usage
With an ASCII domain:
spotspoof lookup example.com
{
"q": "example.com",
"ascii": true,
"puny": false,
"results": [
{
"domain": "exame.com",
"similarity": 82
}
]
}With a Punycode domain:
spotspoof lookup амаzоn.com
{
"q": "амаzоn.com",
"ascii": false,
"puny": true,
"results": [
{
"domain": "amazon.com",
"mappings": [
{
"unicode": "а",
"ascii": "a"
},
{
"unicode": "м",
"ascii": "m"
},
{
"unicode": "а",
"ascii": "a"
},
{
"unicode": "о",
"ascii": "o"
}
],
"is_registered": true
},
{
"domain": "amaz0n.com",
"mappings": [
{
"unicode": "а",
"ascii": "a"
},
{
"unicode": "м",
"ascii": "m"
},
{
"unicode": "а",
"ascii": "a"
},
{
"unicode": "о",
"ascii": "0"
}
],
"is_registered": true
}
]
}Example 2: ASCII usage
spotspoof ascii example.com
{
"q": "example.com",
"ascii": true,
"puny": false,
"results": [
{
"domain": "exame.com",
"similarity": 82
}
]
}Non ASCII domains will not return any results:
spotspoof ascii амаzоn.com
{
"q": "амаzоn.com",
"ascii": true,
"puny": false,
"results": []
}Example 3: Punycode usage
spotspoof idn амаzоn.com
{
"q": "амаzоn.com",
"ascii": false,
"puny": true,
"results": [
{
"domain": "amazon.com",
"mappings": [
{
"unicode": "а",
"ascii": "a"
},
{
"unicode": "м",
"ascii": "m"
},
{
"unicode": "а",
"ascii": "a"
},
{
"unicode": "о",
"ascii": "o"
}
],
"is_registered": true
},
{
"domain": "amaz0n.com",
"mappings": [
{
"unicode": "а",
"ascii": "a"
},
{
"unicode": "м",
"ascii": "m"
},
{
"unicode": "а",
"ascii": "a"
},
{
"unicode": "о",
"ascii": "0"
}
],
"is_registered": true
}
]
}Non Punycode domains will not return any results:
spotspoof idn example.com
{
"q": "example.com",
"ascii": false,
"puny": true,
"results": [
{
"domain": "example.com",
"mappings": [],
"is_registered": true
}
]
}Example 4: Plaintext output usage
Lookup:
spotspoof lookup example.com -t
Domain: exame.com, Similarity: 82IDN Analysis:
spotspoof idn амаzоn.com -t
Domain: amazon.com,
Mappings: a -> а, m -> м, a -> а, o -> о;
Registered: true
Domain: amaz0n.com,
Mappings: a -> а, m -> м, a -> а, 0 -> о
Registered: trueExample 5: CSV output usage
spotspoof lookup example.com --csv
domain,similarity
exame.com,82Check a domain for spoofing:
curl -X POST http://localhost:8080/lookup \
-H "Content-Type: application/json" \
-d '{"domain": "paypai.com"}'
{
"q":"paypai.com",
"ascii":true,
"puny":false,
"results": [
{
"domain":"paypal.com",
"similarity":90
}
]
}ASCII-only lookup:
curl -X POST http://localhost:8080/ascii \
-H "Content-Type: application/json" \
-d '{"domain": "example.com"}'
{
"q":"example.com",
"ascii":true,
"puny":false,
"results": [
{
"domain":"exame.com",
"similarity":82
}
]
}IDN lookup:
curl -X POST http://localhost:8080/idn \
-H "Content-Type: application/json" \
-d '{"domain": "амаzоn.com"}'
{
"q":"амаzоn.com",
"ascii":false,
"puny":true,
"results": [
{
"domain":"amazon.com",
"mappings": [
{
"unicode":"а",
"ascii":"a"
},
{
"unicode":"м",
"ascii":"m"
},
{
"unicode":"а",
"ascii":"a"
},
{
"unicode":"о",
"ascii":"o"
}
],
"is_registered":true
},
{
"domain":"amaz0n.com",
"mappings": [
{
"unicode":"а",
"ascii":"a"
},
{
"unicode":"м",
"ascii":"m"
},
{
"unicode":"а",
"ascii":"a"
},
{
"unicode":"о",
"ascii":"0"
}
],
"is_registered":true
}
]
}Health check:
curl http://localhost:8080/healthz
{
"ok": true
}# Clone the repo
git clone https://github.com/slimpagey/spotspoof-cli.git
cd spotspoof-cli
# Install dependencies
cargo build
# Run tests
cargo test
# Run with logging
RUST_LOG=debug cargo run -- --help# Run all tests
cargo test
# Run with output
cargo test -- --nocapture
# Run specific test
cargo test test_nameThis project uses:
rustfmtfor code formatting:cargo fmtclippyfor linting:cargo clippy
Please run both before submitting a PR.
Contributions are welcome! Please see CONTRIBUTING.md for details.
- Report bugs via GitHub Issues
- Suggest features via GitHub Issues
- Improve documentation
- Submit pull requests
See SECURITY.md for information on reporting security vulnerabilities.
This project is licensed under the MIT License - see the LICENSE file for details.