
urx v0.11.0
Extrahiert URLs aus OSINT-Archiven für Sicherheitseinblicke
Extrahiert URLs aus OSINT-Archiven für Sicherheitserkenntnisse.
Urx ist ein Kommandozeilen-Tool, das URLs aus OSINT-Archiven wie der Wayback Machine und Common Crawl sammelt. Es wurde in Rust auf Effizienz ausgelegt und nutzt asynchrone Verarbeitung, um mehrere Datenquellen schnell abzufragen. Dieses Tool vereinfacht den Prozess der Sammlung von URL-Informationen für eine bestimmte Domain und liefert einen umfassenden Datensatz, der für verschiedene Zwecke genutzt werden kann, einschließlich Sicherheitstests und -analysen.
Funktionen
- URLs aus mehreren Quellen parallel abrufen (Wayback Machine, Common Crawl, OTX, Arquivo.pt)
- Jeden anderen CDX-Indexserver einbinden — nationale Webarchive, ein privates pywb, OutbackCDX — mit
--cdx-endpoint URL, ohne Codeänderung - Standardmäßig ohne Schlüssel: Wayback, Common Crawl, OTX, Arquivo.pt und URLScan (anonym) funktionieren alle ohne API-Schlüssel
- BeVigil-Provider: URLs, die aus entpackten Android-Apps extrahiert wurden — Endpunkte, die kein Webarchiv jemals gecrawlt hat
- Unterstützung für API-Schlüsselrotation bei den Providern VirusTotal und URLScan, um Ratenlimits zu mildern
- Authentifiziertes Testen:
-H,--cookieund--user-agentgelten für jede Anfrage, die urx an das Ziel stellt (--check-status,--extract-links,--extract-js-endpoints,--expand-specs) und werden bewusst niemals an ein Archiv gesendet - Ergebnisse nach Dateiendungen, Teilstring-Mustern oder vollständigen regulären Ausdrücken filtern (
--match-regex/--filter-regex) - Vordefinierte Presets, sowohl nach Dateifamilie ("no-images", "only-js") als auch nach Sicherheitsinteresse ("only-secrets", "only-backup", "only-config", "only-api")
- Archivseitige Filterung: Statuscode, MIME-Typ und Datumsbereich direkt in die CDX-Abfrage einbeziehen, sodass herausgefilterte Captures niemals über das Netzwerk gehen
- Clientseitige Metadatenfilterung (
--meta-*): Filterung nach erstem/letztem Capture-Datum, aufgezeichnetem MIME-Typ und aufgezeichnetem Status einheitlich über jeden Provider, nach der Sammlung - Pfadbezogene Ziele:
urx example.com/shopschiebt den Geltungsbereich direkt in die CDX-Abfrage (url=example.com/shop*), sodass ein Teilbaum einer großen Website nur einen Bruchteil des gesamten Index kostet, statt clientseitig herausgefiltert zu werden - Bug-Bounty-Scope-Dateien (
--scope-file): die eigene*.example.com/!admin.example.com-Liste eines Programms wird wörtlich verwendet, wiederholbar und vereinigt, Ausschlüsse gewinnen immer - URL-Normalisierung und Deduplizierung: Abfrageparameter sortieren, abschließende Schrägstriche entfernen, semantisch identische URLs zusammenführen und Fast-Duplikate zusammenfassen, die sich nur in IDs, Hashes oder Datumsangaben unterscheiden (
--dedup-similar) - Unterstützung für mehrere Ausgabeformate: Klartext, JSON, JSON Lines, CSV und
wordlist— die Pfadsegmente und Parameternamen, aus denen das Ziel aufgebaut ist, ohne IDs, Hashes und Datumsangaben - Parameter- und Fuzz-Ansichten:
--params(das gesamte Parameterinventar des Ziels),--params-by-endpoint(welcher Endpunkt was annimmt) und--fuzz-placeholder FUZZ(eine templatisierte URL pro Parametersignatur, bereit für ffuf oder dalfox) - Archiv-Capture-Metadaten:
first_seen,last_seen,mime,archive_statusunddigestkommen mit jeder URL zurück, die ein CDX-Archiv gemeldet hat, ohne zusätzliche Netzwerkkosten - Streaming-Ausgabe (
--stream): URLs werden geschrieben, sobald jeder Provider sie meldet, sodass eine Pipeline sofort mit der Arbeit beginnt, statt auf das langsamste Archiv zu warten - Unterstützung für direkte Dateieingabe: URLs direkt aus WARC-Dateien, URLTeam-komprimierten Dateien und Textdateien lesen
- Ergebnisse auf der Konsole oder in einer Datei ausgeben oder über stdin für die Pipeline-Integration streamen
- URL-Testing:
- URLs anhand von HTTP-Statuscodes und Mustern filtern und validieren.
- Zusätzliche Links aus gesammelten URLs extrahieren — Anker, Skripte, Stylesheets, Formularaktionen, Iframes, Bilder, Medienquellen, Objekte, Embeds und Meta-Refresh-Ziele
- Die archivierten Antwortkörper gesammelter URLs durchsuchen (
--archive-body), sodass Seiten, die nicht mehr existieren, noch die Links preisgeben, die sie enthielten — eine Anfrage pro unterschiedlichem Body, dank CDX-Digest-Deduplizierung - Mit
--extract-js-endpointsauch das archivierte JavaScript durchsuchen: Ein Bundle, das nach Build-Hash benannt ist, liefert 404, sobald die Website neu deployt wird, und das Archiv ist der einzige Ort, an dem seine API-Oberfläche noch existiert - Die wiedergegebenen Bodies (
--archive-body-dir) als Korpus behalten, um nach dem zu greppen, wonach kein Link-Extraktor sucht — Entwicklerkommentare, eingebettete Zugangsdaten, interne Hostnamen — ohne zusätzliche Anfragen - API-Spezifikationen erweitern (
--expand-specs): OpenAPI 3.x-, Swagger 2.0- und GraphQL-Introspection-Dokumente, JSON oder YAML, in jede Route umgewandelt, die sie beschreiben — eine Anfrage kauft die gesamte dokumentierte Oberfläche - Antwort-Metadaten:
--check-statuszeichnet auchLocation,Content-LengthundContent-Typeauf, und--check-titlefügt den HTML-<title>hinzu
- Archivierte robots.txt- und sitemap.xml-Erkennung (
--archived-discovery): jede unterschiedliche Version, die die Wayback Machine besitzt, sodass einDisallow:aus 2015 noch die Pfade nennt, die die Website seither nicht mehr erwähnt - Caching und inkrementelles Scannen:
- Lokales SQLite- oder entferntes Redis-Caching, um erneutes Scannen von Domains zu vermeiden
- Inkrementeller Modus, um nur neue URLs seit dem letzten Scan zu entdecken
- Konfigurierbare Cache-TTL und automatische Bereinigung abgelaufener Einträge
urx cache-Unterbefehl zum Inspizieren und Pflegen des Caches:stats,list,prune,drop <domain>,clear

Installation
Aus Cargo```bash
https://crates.io/crates/urx
cargo install urx
### Über Homebrew```bash
# https://formulae.brew.sh/formula/urx
brew install urx
Aus der Quelle```bash
git clone https://github.com/hahwul/urx.git cd urx cargo build --release
Das kompilierte Binary ist unter `target/release/urx` verfügbar.
### Aus Docker
[ghcr.io/hahwul/urx](https://github.com/hahwul/urx/pkgs/container/urx)
### Shell-Vervollständigungen
`urx` generiert sein eigenes Vervollständigungsskript, sodass es immer mit den Flags
des Binaries übereinstimmt, das Sie tatsächlich installiert haben.```bash
# zsh — any directory on your $fpath works
urx --completions zsh > ~/.zfunc/_urx
# (make sure ~/.zfunc is on the fpath, then `compinit`)
# bash
urx --completions bash > ~/.local/share/bash-completion/completions/urx
# fish
urx --completions fish > ~/.config/fish/completions/urx.fish
powershell und elvish werden ebenfalls unterstützt. Das Flag benötigt keine Zieldomain.
Man-Page```bash
urx --manpage > ~/.local/share/man/man1/urx.1 man urx
## Verwendung
### Grundlegende Verwendung```bash
# Scan a single domain
urx example.com
# Scan multiple domains
urx example.com example.org
# Scan domains from a file
cat domains.txt | urx
Optionen```
Usage: urx [OPTIONS] [DOMAINS]... [COMMAND]
Commands: cache Inspect and maintain the URL cache: stats, list, prune, drop ..., clear
Arguments: [DOMAINS]... Domains to fetch URLs for
Options: -c, --config Config file to load --provider-config Separate provider config file holding only API keys (default: $XDG_CONFIG_HOME/urx/provider-config.toml). CLI/env > provider-config > main config. --completions Print a shell completion script (bash, zsh, fish, powershell, elvish) to stdout and exit --manpage Print the roff man page to stdout and exit -h, --help Print help -V, --version Print version
Input Options:
--files ... Read URLs directly from files (supports WARC, URLTeam compressed, and text files)
--domain-list File of newline-separated domains to scan (repeatable; merged with positional DOMAINS and stdin; # comments allowed)
Output Options:
-o, --output Output file to write results
--output-dir Write one file per domain into this directory (extension matches --format). Coexists with --output / stdout.
-f, --format Output format: "plain", "json" (one array), "jsonl" (one JSON object per line), "csv", "wordlist" (path segments and parameter names, deduplicated and sorted) [default: plain]
--stream Write URLs as each provider reports them instead of once at the end (unsorted; bypasses cache; rejects options needing the full result set)
--merge-endpoint Merge endpoints with the same path and merge URL parameters
--normalize-url Normalize URLs for better deduplication (sorts query parameters, removes trailing slashes)
--dedup-similar Collapse URLs that differ only in variable data (numeric ids, UUIDs, hashes, dates, query values)
--params Replace the URL list with every query parameter name the run saw, once each
--params-by-endpoint
One line per endpoint: the endpoint and the comma-separated union of the parameter names seen on it (id-looking path segments collapse to {id})
--fuzz-placeholder
Replace every query parameter value with VALUE, keeping one URL per parameter signature — output you can feed straight to ffuf or dalfox
Provider Options:
--providers
Providers to use (comma-separated, e.g., "wayback,cc,otx,arquivo,vt,urlscan") [default: wayback,cc,otx]
--exclude-providers <EXCLUDE_PROVIDERS>
Providers to exclude (comma-separated). Wins on conflict with --providers / --all-providers.
--all-providers
Enable every supported provider. API-keyed providers only activate when a key is available.
--list-providers
List every supported provider then exit.
--subs
Include subdomains when searching
--cc-index <CC_INDEX>
Common Crawl index to use; accepts comma-separated list to query multiple indexes in parallel (e.g. CC-MAIN-2026-17,CC-MAIN-2025-51). latest (the default) resolves the newest via collinfo.json. [default: latest]
--cdx-endpoint
Query an additional CDX index server (any pywb, OutbackCDX, or classic Internet-Archive-style CDX API) by its full API URL, e.g. https://vefsafn.is/cdx. Repeatable. Each endpoint becomes a provider with id cdx:<host> and honours --subs, --from/--to and the --archive-* filters. See "Custom CDX Endpoints" below
--cdx-dialect
Which CDX dialect the --cdx-endpoint servers speak: pywb or classic. Unset: urx probes each endpoint once and falls back to pywb when the answer is ambiguous
--from
Restrict every CDX-backed provider (wayback, cc, arquivo, --cdx-endpoint) to captures at or after DATE (YYYY/YYYYMM/YYYYMMDD/YYYYMMDDhhmmss). Alias: --wayback-from
--to
Restrict every CDX-backed provider to captures at or before DATE (same format as --from). Alias: --wayback-to
--archive-status
Keep only captures the archive recorded with this HTTP status code (e.g. "200"). Applied by the CDX index itself, so unlike --include-status it costs no extra requests. A multi-value list works on wayback only — see "Archive-side Filtering" below
--archive-exclude-status
Drop captures the archive recorded with these HTTP status codes (comma-separated, e.g. "404,500"). Multi-value works on every CDX provider
--archive-mime
Keep only captures with this recorded MIME type (e.g. "application/json"). Catches endpoints with no file extension, which -e/--extensions cannot
--archive-exclude-mime
Drop captures with these recorded MIME types (comma-separated, e.g. "text/html,image/png")
--vt-api-key <VT_API_KEY>
API key for VirusTotal (can be used multiple times for rotation, can also use URX_VT_API_KEY environment variable with comma-separated keys)
--urlscan-api-key <URLSCAN_API_KEY>
Optional API key for Urlscan; the provider also works anonymously (rate-limited ~30 req/min per IP). Can be used multiple times for rotation, or via URX_URLSCAN_API_KEY (comma-separated keys)
--github-api-key <GITHUB_API_KEY>
Personal access token for the GitHub Code Search provider (also reads URX_GITHUB_API_KEY, comma-separated for rotation)
--bevigil-api-key <BEVIGIL_API_KEY>
API key for BeVigil, which returns URLs extracted from unpacked Android apps (also reads URX_BEVIGIL_API_KEY, comma-separated for rotation). Required for the bevigil provider
Discovery Options:
--exclude-robots
Exclude robots.txt discovery
--exclude-sitemap
Exclude sitemap.xml discovery
--archived-discovery
Also read every distinct archived version of robots.txt and sitemap.xml the Wayback Machine holds
--archived-discovery-limit
Maximum archived documents fetched per domain by each archived provider (nested sitemaps count) [default: 50]
Display Options:
-v, --verbose Show verbose output
--silent Silent mode (no output)
--no-progress No progress bar
--no-color Disable ANSI color in the progress UI and output (NO_COLOR is also honored)
--show-sources Annotate output URLs with the providers that returned them
--show-meta Annotate plain-text URLs with the archive capture metadata
--stats Print a per-provider summary to stderr at end of run
Filter Options:
-p, --preset
Filter Presets (e.g., "no-resources,no-images,no-audio,only-js,only-style,only-secrets,only-backup,only-config,only-api")
-e, --extensions
Filter URLs to only include those with specific extensions (comma-separated, e.g., "js,php,aspx")
--exclude-extensions <EXCLUDE_EXTENSIONS>
Filter URLs to exclude those with specific extensions (comma-separated, e.g., "html,txt")
--patterns
Filter URLs to only include those containing specific patterns (comma-separated)
--exclude-patterns <EXCLUDE_PATTERNS>
Filter URLs to exclude those containing specific patterns (comma-separated)
--match-regex
Keep only URLs matching this regular expression (repeatable, ORed; case-sensitive; never comma-split)
--filter-regex
Drop URLs matching this regular expression (repeatable; one match is enough)
--show-only-host
Only show the host part of the URLs
--show-only-path
Only show the path part of the URLs
--show-only-param
Only show the parameters part of the URLs
--min-length <MIN_LENGTH>
Minimum URL length to include
--max-length <MAX_LENGTH>
Maximum URL length to include
--strict
Enforce exact host validation (default)
--no-strict
Disable host validation (keep URLs on any host a provider returns). Wins over --strict. A target's path scope still applies: only the host check is waived
--scope-file
Bug-bounty scope file: one host pattern per line, ! to exclude, *.example.com for a wildcard (which covers the apex too), # for a comment. Repeatable and unioned; exclusions always win. See "Scope Files" below
--meta-first-seen-after
Keep URLs whose oldest archived capture is on or after DATE (YYYY/YYYYMM/YYYYMMDD/YYYYMMDDhhmmss)
--meta-first-seen-before
Keep URLs whose oldest archived capture is on or before DATE
--meta-last-seen-after
Keep URLs whose newest archived capture is on or after DATE — "still alive as of"
--meta-last-seen-before
Keep URLs whose newest archived capture is on or before DATE — "dead since"
--meta-mime
Keep only URLs whose archived MIME type is one of these (comma-separated; image/* matches any subtype)
--meta-exclude-mime
Drop URLs whose archived MIME type is one of these
--meta-status
Keep only URLs whose archived status code matches (comma-separated; 20x / 5xx patterns)
--meta-exclude-status
Drop URLs whose archived status code matches
Network Options:
--network-scope <NETWORK_SCOPE> Control which components network settings apply to (all, providers, testers, or providers,testers) [default: all]
--proxy Use proxy for HTTP requests (format: http://proxy.example.com:8080)
--proxy-auth <PROXY_AUTH> Proxy authentication credentials (format: username:password)
--insecure Skip SSL certificate verification (accept self-signed certs)
--random-agent Use a random User-Agent for HTTP requests
-H, --header <NAME: VALUE> Extra request header, repeatable; sent only on requests urx makes to the target, never to an archive
--cookie Cookie header for requests to the target; shorthand for -H "Cookie: ..."
--user-agent User-Agent for requests to the target, overriding the default and --random-agent
--timeout Request timeout in seconds [default: 120]
--retries Number of retries for failed requests [default: 2]
--parallel Maximum domains fetched concurrently per provider (and concurrent URL tests); a provider's --rate-limit is shared across them [default: 5]
--rate-limit <RATE_LIMIT> Rate limit (requests per second)
--rate-limit-by Per-provider rate overrides (e.g. vt=1,wayback=10); falls back to --rate-limit for unlisted providers
--max-time <MAX_TIME> Global ceiling on provider enumeration time in seconds (0 = unlimited) [default: 0]
Testing Options:
--check-status
Check HTTP status code of collected URLs [aliases: ----cs]
--check-title
Also record each response's HTML while checking statuses; implies --check-status
--include-status <INCLUDE_STATUS>
Include URLs with specific HTTP status codes or patterns (e.g., --is=200,30x) [aliases: ----is]
--exclude-status <EXCLUDE_STATUS>
Exclude URLs with specific HTTP status codes or patterns (e.g., --es=404,50x,5xx) [aliases: ----es]
--extract-links
Extract additional links from collected URLs (requires HTTP requests)
--extract-js-endpoints
Fetch collected JavaScript files and extract the endpoint paths and URLs found in their string literals (requires HTTP requests); with --archive-body this also mines the archived copy of each script
--max-js-files
Maximum number of files --extract-js-endpoints will fetch (0 = unlimited) [default: 500]
--archive-body
Fetch the archived body of each collected URL from the Wayback Machine and extract the links inside it (works for pages that no longer exist)
--archive-body-limit
Maximum number of archived bodies --archive-body fetches per run; bounds distinct bodies, not URLs [default: 500]
--archive-body-dir
Keep every body --archive-body replays in DIR, with an index.jsonl mapping each file back to its URL, capture and content type
--expand-specs
Fetch the API specification documents among the collected URLs (OpenAPI, Swagger, GraphQL introspection; JSON or YAML) and expand every route they document into a URL. See "Expanding API Specifications" below
--max-spec-files
Maximum number of specification documents --expand-specs will fetch (0 = unlimited) [default: 50]
Cache Options:
--incremental Enable incremental scanning mode (only return new URLs compared to previous scans)
--cache-type Cache backend: sqlite or redis [default: sqlite]
--cache-path Path for the SQLite cache database
--redis-url Redis connection URL for remote caching
--cache-ttl Cache time-to-live in seconds [default: 86400]
--no-cache Disable caching entirely
Notification Options:
--notify POST a run summary to this webhook when the run ends (repeatable; also URX_NOTIFY_URL, provider-config notify_url, or [notify].url)
--notify-on <NOTIFY_ON> When to send: new (only if URLs were emitted), always, or never [default: new]
--notify-format <NOTIFY_FORMAT> Payload shape: json (urx summary), slack ({"text"}), or discord ({"content"}) [default: json]
`--extract-links` liest jedes Tag mit URL, nicht nur Anker: `<a href>`,
`<script src>`, `<link href>`, `<form action>`, ``, ``,
`<source src>`, `<object data>`, `<embed src>` und `<meta http-equiv="refresh">`
-Ziele. Relative URLs werden gegen die Seite aufgelöst (unter Berücksichtigung von `<base href>`),
Duplikate werden zusammengefasst, und entdeckte Links durchlaufen dieselben Filter
und Host-Validierungen wie der Rest des Durchlaufs. Siehe
[docs/content/guide/cli-options.md](https://github.com/hahwul/urx/blob/main/docs/content/guide/cli-options.md) für die
vollständige Tabelle.
`--extract-js-endpoints` geht einen Schritt weiter und liest das JavaScript
selbst: jede gesammelte URL, die wie ein Skript aussieht, wird abgerufen und ihre
String-Literale werden nach den Pfaden und URLs durchsucht, die die App aufruft —
`fetch("/api/v2/users")`, `axios.post("/graphql")`, das statische Präfix von
`` `/api/orders/${id}` ``. Dies sind die Endpunkte, die nie im HTML erscheinen.
Die Ausgabe wird aggressiv entrauscht (MIME-Typen, Modul-Spezifizierer, Base64,
CSS-Werte, Regex-Fragmente und mehr werden verworfen), jeder Body ist auf
10 MiB begrenzt, die Anzahl der abgerufenen Dateien wird durch `--max-js-files` begrenzt, und die
entdeckten Endpunkte durchlaufen dieselben Filter und Host-Validierungen wie alles
andere. Die vollständige Extraktions- und Rauschunterdrückungsrichtlinie finden Sie in
[docs/content/guide/cli-options.md](https://github.com/hahwul/urx/blob/main/docs/content/guide/cli-options.md#javascript-endpoint-extraction).
`--archive-body` führt dieselbe Extraktion über die Bodies durch, die die Wayback Machine
*gespeichert* hat, statt über die Live-Seite, sodass eine Seite, die vor Jahren gelöscht wurde,
immer noch die enthaltenen Links liefert. Siehe
[Mining Archived Response Bodies](#mining-archived-response-bodies).
### Beispiele```bash
# Save results to a file
urx example.com -o results.txt
# Output in JSON format
urx example.com -f json -o results.json
# Filter for JavaScript files only
urx example.com -e js
# Exclude HTML and text files
urx example.com --exclude-extensions html,txt
# Filter for API endpoints
urx example.com --patterns api,v1,graphql
# Exclude specific patterns
urx example.com --exclude-patterns static,images
# Use Fileter Preset (similar to --exclude-extensions=png,jpg,.....)
urx example.com -p no-images
# Use specific providers
urx example.com --providers wayback,otx
# Add the keyless Arquivo.pt (Portuguese web archive) provider
urx example.com --providers wayback,cc,otx,arquivo
# Query another CDX index server alongside the defaults (id: cdx:vefsafn.is)
urx example.is --cdx-endpoint https://vefsafn.is/cdx
# ...or on its own, rate-limited, with the archive-side filters it shares with wayback/cc
urx example.is --cdx-endpoint https://vefsafn.is/cdx --providers cdx:vefsafn.is \
--rate-limit-by cdx:vefsafn.is=1 --from 2020 --archive-status 200
# URLScan works without a key (anonymous, rate-limited); a key just raises limits
urx example.com --providers urlscan
# BeVigil: endpoints pulled out of unpacked Android apps (key required; auto-enables the provider)
URX_BEVIGIL_API_KEY=*** urx example.com
# Using VirusTotal and URLScan providers
# 1. Explicitly add to providers (with API keys via command line)
urx example.com --providers=vt,urlscan --vt-api-key=*** --urlscan-api-key=***
# 2. Using environment variables for API keys
URX_VT_API_KEY=*** URX_URLSCAN_API_KEY=*** urx example.com --providers=vt,urlscan
# 3. Auto-enabling: providers are automatically added when API keys are provided
urx example.com --vt-api-key=*** --urlscan-api-key=*** # No need to specify in --providers
# 4. Multiple API key rotation (to mitigate rate limits)
# Using repeated flags for multiple keys
urx example.com --vt-api-key=key1 --vt-api-key=key2 --vt-api-key=key3
# Using environment variables with comma-separated keys
URX_VT_API_KEY=key1,key2,key3 URX_URLSCAN_API_KEY=ukey1,ukey2 urx example.com
# Combining CLI flags and environment variables (CLI keys are used first)
URX_VT_API_KEY=env_key1,env_key2 urx example.com --vt-api-key=cli_key1 --vt-api-key=cli_key2
# URLs from robots.txt and sitemap.xml are included by default
# Exclude URLs from robots.txt files
urx example.com --exclude-robots
# Exclude URLs from sitemap
urx example.com --exclude-sitemap
# Also read every archived version of robots.txt and sitemap.xml, so paths the
# site once listed and has since removed come back
urx example.com --archived-discovery
# Only the versions captured in a given era
urx example.com --archived-discovery --from 2014 --to 2016 --exclude-sitemap
# Include subdomains
urx example.com --subs
# Check status of collected URLs
urx example.com --check-status
# Read URLs directly from a text file
urx --files urls.txt
# Combine file input with filtering
urx --files urls.txt --patterns api,admin -f json
# Extract additional links from collected URLs
# (anchors, scripts, stylesheets, form actions, iframes, images, media
# sources, objects, embeds, and meta-refresh targets)
urx example.com --extract-links
# Discovered links go through the same filters as everything else, so this
# keeps only the JavaScript the pages reference
urx example.com --extract-links -e js
# Read the collected JavaScript and pull out the API paths it calls
urx example.com --extract-js-endpoints --patterns api
# Chain them: collect the site's bundles, then mine those for endpoints
urx example.com --extract-links --extract-js-endpoints --max-js-files 100
# Mine the links inside the *archived* bodies instead — dead pages included.
# One request per distinct body; the limit bounds bodies, not URLs
urx example.com --archive-body --archive-body-limit 200 --rate-limit 5
# Network configuration
urx example.com --proxy http://localhost:8080 --timeout 60 --parallel 10 --insecure
# Advanced filtering
urx example.com -e js,php --patterns admin,login --exclude-patterns logout,static --min-length 20
# HTTP Status code based filtering (live requests: urx re-fetches each URL)
urx example.com --include-status 200,30x,405 --exclude-status 20x
# Archive-side filtering (free: the CDX index already knows these)
# Skip everything the archive recorded as a 404 — no extra requests
urx example.com --archive-exclude-status 404
# Only captures the archive served as JSON — finds extensionless API endpoints
urx example.com --archive-mime application/json
# Drop HTML to leave assets and endpoints behind
urx example.com --archive-exclude-mime text/html
# Restrict the crawl window across wayback, cc, arquivo, and any --cdx-endpoint alike
urx example.com --from 2023 --to 2024
# Disable host validation
urx example.com --strict false
# URL normalization and deduplication
# Normalize URLs by sorting query parameters and removing trailing slashes
urx example.com --normalize-url
# Combine normalization with endpoint merging for comprehensive deduplication
urx example.com --normalize-url --merge-endpoint
# URL normalization with file input
urx --files urls.txt --normalize-url
# Collapse /post/1, /post/2, /post/99999 ... into a single representative line
urx example.com --dedup-similar
# Regular-expression filtering (repeat either flag; they are never comma-split)
urx example.com --match-regex '/api/v[0-9]+/'
urx example.com --match-regex '\.php$' --match-regex '\.aspx$'
urx example.com --filter-regex '/(assets|static)/'
# Regexes are case-sensitive; ask for insensitivity explicitly
urx example.com --match-regex '(?i)admin'
# Security presets: match by path shape as well as by extension
urx example.com -p only-secrets # /.env, /.git/config, id_rsa, *.pem
urx example.com -p only-backup # *.bak, *.sql, /backup/, index.php~
urx example.com -p only-config # *.yaml, web.config, .htaccess, Dockerfile
urx example.com -p only-api # /api/, /v1/, /graphql, /swagger, *.wsdl
# Scope files: a bug bounty program's own host list, used verbatim
urx example.com --subs --scope-file scope.txt
# Metadata filters, applied after collection so every provider is covered
urx example.com --providers wayback --meta-last-seen-after 2024 --meta-exclude-mime 'image/*'
urx example.com --providers wayback --meta-mime application/json --meta-status 200
# What parameters does this target take, and where?
urx example.com --params
urx example.com --params-by-endpoint
# One templated URL per parameter signature, straight into a fuzzer
urx example.com --fuzz-placeholder FUZZ | ffuf -w - -u FUZZ
# A target-specific wordlist instead of a URL list
urx example.com --subs -f wordlist -o words.txt
# Open the API specifications the sweep found and expand every route in them
urx example.com -p only-api --expand-specs
# Status checks also keep the response head; --check-title adds the <title>
urx example.com --check-status -f jsonl
urx example.com --check-title --show-meta
# Inspect and maintain the cache
urx cache stats
urx cache drop example.com
Einen Lauf auf einen Pfad beschränken
Ein Ziel kann einen Pfad angeben, und es bedeutet, was es sagt: urx example.com/shop
sammelt den Teil der Website unter /shop.```bash
urx example.com/shop
urx https://example.com/api/v2 # a pasted URL works too
Das ist kein Filter, der nachträglich angewendet wird. Ein CDX-Index beantwortet Präfixabfragen nativ, also sendet urx `url=example.com/shop*` und das Archiv überträgt den Rest der Website nie über das Netzwerk — bei einem großen Ziel ist das der Unterschied zwischen ein paar hundert Zeilen und ein paar hunderttausend. Anbieter, die einen Pfad nicht in ihrer Abfrage ausdrücken können (OTX, VirusTotal, urlscan, GitHub, BeVigil, ZoomEye), werden nach dem Host gefragt und ihre Antworten werden anschließend eingegrenzt, ebenso wie die Ergebnisse eines `--subs`-Laufs, bei dem die `*.host`-Form und ein Pfadpräfix nicht in einer CDX-Abfrage kombiniert werden können.
Scope bedeutet *auf oder unter* dem Pfad: `/shop` und `/shop/cart` sind enthalten, `/shopping` nicht. Groß-/Kleinschreibung wird ignoriert, weil ein CDX-Server die gesamte URL in Kleinbuchstaben umwandelt, wenn er seinen Indexschlüssel erstellt — `example.com/Shop*` und `example.com/shop*` liefern dieselben Zeilen, alle in Kleinbuchstaben geschrieben, sodass eine groß-/kleinschreibungssensitive Prüfung alles verwerfen würde, was das Archiv gerade zurückgegeben hat. Ein Query-String oder Fragment im Ziel wird verworfen — diese schränken eine Anfrage ein, nicht einen Scope.
> Hinweis: urx hat früher den Pfad aus einem Ziel verworfen, sodass
> `urx https://example.com/shop` die gesamte Domain `example.com` scannte. Jetzt
> wird `/shop` gescannt. Übergeben Sie nur den Host für das alte Verhalten; ein Lauf, dessen Ziel
> einen Pfad enthält, gibt dies auf stderr aus.
### Filterung mit regulären Ausdrücken
`--patterns` / `--exclude-patterns` sind einfache Teilstring-Tests: beide Seiten werden
in Kleinbuchstaben umgewandelt, und jedes Metazeichen ist ein Literal. `--match-regex` /
`--filter-regex` sind die Regex-Gegenstücke, und sie unterscheiden sich in drei Punkten, die
es wert sind, in Erinnerung zu bleiben:
| | `--patterns` | `--match-regex` |
|---|---|---|
| Übereinstimmung | Teilstring | vollständige [Regex-Syntax](https://docs.rs/regex/latest/regex/#syntax) |
| Groß-/Kleinschreibung | insensitive (beide Seiten in Kleinbuchstaben) | **sensitive** — verwenden Sie `(?i)`, um dies zu deaktivieren |
| Mehrere Werte | ein kommagetrennter Schalter | den Schalter wiederholen; Kommas werden nie aufgeteilt |
Beide Regex-Schalter werden gegen die **gesamte URL-Zeichenkette** ausgewertet, wie sie gesammelt wurde
(Schema, Host, Pfad und Query), sodass sowohl `^https://` als auch `\.js$` funktionieren.
Ausschluss gewinnt: eine URL, die auf `--filter-regex` passt, wird verworfen, selbst wenn
`--match-regex` ebenfalls darauf gepasst hat. Ein fehlerhafter Ausdruck lässt den Lauf beim
Start fehlschlagen, bevor ein Archiv abgefragt wird.
### Scope-Dateien
Der Scope eines Bug-Bounty-Programms ist eine Liste von Hosts, und jede Plattform schreibt sie
auf die gleiche Weise. `--scope-file` nimmt diese Liste wörtlich entgegen, anstatt Sie
sie manuell in verankerte Regex-Alternationen zu übersetzen — wobei eine falsche
Verankerung den Scope stillschweigend *erweitert*, anstatt fehlzuschlagen.```text
# scope.txt — in scope
*.example.com
api.example.org
# out of scope, even though the wildcard above covers them
!admin.example.com
!*.internal.example.com
Ihre Antwort ist leer. Bitte geben Sie den zu übersetzenden Inhalt an.```bash
urx example.com --subs --scope-file scope.txt
urx --domain-list targets.txt --subs --scope-file scope-a.txt --scope-file scope-b.txt
`*.example.com` trifft sowohl den Apex als auch alles darunter (die
Bug-Bounty-Lesart, die eine Scope-Tabelle einer Plattform meint); ein bloßer Host
trifft genau diesen Host; ein einzelnes `*` macht die Datei zu einer reinen Deny-Liste;
Ausschlüsse gewinnen immer; `#` beginnt einen Kommentar. Alles, was urx nicht
berücksichtigen kann — ein Port, ein Pfad, ein Wildcard in der Mitte — ist ein
Startfehler, der die Datei und Zeile benennt, statt einen stillschweigend
erweiterten Scope. Der Filter gilt für jeden Provider und für extrahierte Links,
und er kombiniert sich mit `--strict`, statt es zu ersetzen, sodass eine
`*.example.com`-Scope-Zeile weiterhin `--subs` benötigt.
### Archiv-Metadaten-Filter
`--from`/`--to` und die `--archive-*`-Prädikate werden in die eigene Abfrage des
Archivs geschoben, was sie kostenlos macht und sie zugleich auf CDX-gestützte
Provider beschränkt — und die beiden CDX-Dialekte unterscheiden sich so stark,
dass eine positive Mehrwertliste (`--archive-status 200,301`) auf pywb-Servern
nicht erfüllbar ist. Die acht `--meta-*`-Filter laufen stattdessen *nach* der
Sammlung, über eine zusammengeführte Menge von Capture-Metadaten pro URL, sodass
sie einheitlich für jeden Provider gelten.```bash
# Endpoints still being captured recently, with HTML and images out of the way
urx example.com --providers wayback --meta-last-seen-after 2024 --meta-exclude-mime 'text/html,image/*'
# Pages that died: nothing captured since 2019
urx example.com --providers wayback --meta-last-seen-before 2019
# JSON the archive served successfully
urx example.com --providers wayback --meta-mime application/json --meta-status 200
# First archived during 2020 (partial dates pad to the start / end of the period)
urx example.com --providers wayback --meta-first-seen-after 2020 --meta-first-seen-before 2020
URLs ohne Metadaten — die Nicht-CDX-Provider, --files-Eingabe, Cache-Treffer
— werden nach der Richtung des Prädikats aufgeteilt: Ein positives Prädikat kann
nicht durch einen fehlenden Wert erfüllt werden, also wird die URL verworfen; eine
Ausschlussbedingung verwirft nur, was positiv übereinstimmt, also überlebt sie.
--verbose meldet die Aufteilung, und wenn fehlende Metadaten die gesamte
Ergebnismenge ausmachen, sagt urx dies auch ohne -v, weil ein Cache-Treffer
sonst einen leeren Lauf wie ein Ziel ohne Fund erscheinen lässt.
Kollabieren von Fast-Duplikaten
Ein Archiv gibt bereitwillig /post/1 bis /post/99999 zurück. Sie sind ein
Endpunkt, und --dedup-similar gibt eine Zeile für sie aus. Ein Pfadsegment wird
als Daten — nicht als Teil der Route — behandelt, wenn es vollständig eines der
folgenden ist:
- eine Folge von Ziffern (
/post/1, /page/42)
- eine UUID (
/u/550e8400-e29b-41d4-a716-446655440000)
- ein 32/40/64-stelliger Hex-Digest (md5, sha1, sha256)
- ein getrenntes Datum (
/blog/2024-01-02/)
- ein langes Token mit gemischter Groß-/Kleinschreibung und Ziffern darin (Session-IDs, signierte Blobs)
Segmente, die lediglich Ziffern enthalten, bleiben bestehen, sodass /api/v1/ und /api/v2/ weiterhin zwei Endpunkte sind, und ein Slug in Kleinbuchstaben ist Prosa und kein Token. Query-Strings werden nur nach Parameter-Namen gruppiert: ?q=cats&page=1 und ?q=dogs&page=7 kollabieren, während ?q=cats allein dies nicht tut — das Weglassen eines Parameters ändert die Anfrage.
Der Überlebende jeder Gruppe ist ihre lexikografisch kleinste URL, sodass zwei Läufe über dieselben Daten dasselbe ausgeben. --verbose meldet, wie viele URLs kollabiert wurden. Die Option ist unabhängig von --normalize-url und --merge-endpoint und kombiniert sich mit beiden; alle drei benötigen die vollständige Ergebnismenge, sodass keine von ihnen mit --stream funktioniert.
Parameter- und Fuzz-Ansichten
--show-only-param schneidet lediglich den Query-String von jeder URL ab, was die erste Frage eines Testers nicht beantworten kann: Welche Parameter nimmt dieses Ziel entgegen? Drei Ansichten beantworten sie, aufgebaut auf derselben Gruppierung, die --dedup-similar verwendet.```console
$ urx example.com --params
page
q
ref
sort
utm_source
$ urx example.com --params-by-endpoint
https://example.com/post/{id} ref,utm_source
https://example.com/search page,q,sort
$ urx example.com --fuzz-placeholder FUZZ
https://example.com/post/1?ref=FUZZ
https://example.com/post/2?utm_source=FUZZ
https://example.com/search?q=FUZZ&page=FUZZ
https://example.com/search?q=FUZZ&sort=FUZZ
`--params-by-endpoint` fasst id-artige Pfadsegmente zu `{id}` zusammen, genau wie
`--dedup-similar` es tut, und schreibt den Endpunkt vollständig aus, weil urx
routinemäßig mehrere Hosts in einem Durchlauf scannt. `--fuzz-placeholder` behält eine URL pro
Parametersignatur und behält ihren echten Pfad bei – ein `{id}` würde nicht geroutet werden –,
sodass die Ausgabe direkt in einen Fuzzer eingespeist werden kann:```bash
urx example.com --fuzz-placeholder FUZZ | ffuf -w - -u FUZZ
urx example.com --fuzz-placeholder FUZZ | dalfox pipe
Alle drei benötigen das vollständige Ergebnisset, daher sind sie nur im Batch-Modus verfügbar und schließen sich gegenseitig sowie mit den --show-only-*-Ansichten aus.
Wordlist-Ausgabe
-f wordlist verwandelt einen Lauf in eine zielspezifische Wordlist: jedes gesehene Pfadsegment und jeder Query-Parameter-Name, über den gesamten Lauf dedupliziert und sortiert, ein Begriff pro Zeile.```bash
urx example.com --subs -f wordlist -o words.txt
ffuf -w words.txt -u https://example.com/FUZZ
Segmente, die eher wie Daten als wie Routennamen aussehen, werden weggelassen, wobei die Testgruppen `--dedup-similar` wiederverwendet werden — eine Wortliste voller `4711`, UUIDs, Daten und Session-Tokens ist schlechter als gar keine Wortliste, da jedes dieser Wörter auf genau einem Ziel existiert. Ein Segment, dessen Stamm ein Identifier ist, entfällt ebenfalls
(`article-1234.html`). Groß-/Kleinschreibung bleibt erhalten: Pfadsegmente sind auf den meisten Origins case-sensitive, sodass die Kleinschreibung von `WebResource.axd` ein Wort erzeugen würde, das überall, wo es versucht wird, 404 liefert. Die Vereinigung muss über die vollständige Menge gebildet werden, daher ist das Format nur im Batch-Modus möglich.
### Streaming-Ausgabe
Standardmäßig sammelt urx alles, filtert dann, sortiert und gibt einmal aus. Bei einem großen Ziel bedeutet das gar keine Ausgabe, bis das langsamste Archiv fertig ist.
`--stream` schreibt jede URL in dem Moment, in dem der Provider, der sie meldet, zurückkommt:```bash
# Matches start appearing immediately instead of after the slowest provider
urx big-target.com --stream | grep admin
# Line-delimited JSON stays valid while it is still being written
urx big-target.com --stream -f jsonl | jq -r 'select(.url | test("/api/")) | .url'
Gestreamte URLs durchlaufen exakt dieselben Filter wie ein Batch-Lauf und werden weiterhin dedupliziert. Zwei Dinge unterscheiden sich:
- Reihenfolge. Ergebnisse treffen in der Reihenfolge des Abschlusses der Provider ein, daher ist die Ausgabe unsortiert. Leite sie durch
sort, wenn du eine Sortierung benötigst.
- Umfang. Optionen, die das vollständige Ergebnisset benötigen, werden vorab abgelehnt (mit einer Meldung, die jede einzelne benennt):
--merge-endpoint, --dedup-similar, --check-status /
--include-status / --exclude-status, --extract-links,
--extract-js-endpoints, --archive-body, --expand-specs,
--incremental, --show-sources, --show-meta, die --meta-*-Filter,
--params, --params-by-endpoint, --fuzz-placeholder, --output-dir und
--files. Caching wird umgangen;
--format json wird zugunsten von jsonl abgelehnt, weil ein JSON-Array wissen muss, welcher Eintrag der letzte ist, und --format wordlist, weil kein Begriff als neu bekannt sein kann, bis jede URL eingetroffen ist.
Da die Batch-Ergebnismap in diesem Modus nie befüllt wird, hält ein gestreamter Lauf auch deutlich weniger im Speicher — nur das Dedup-Set der bereits geschriebenen URLs.
Archiv-Capture-Metadaten
Ein CDX-Index zeichnet mehr als die URL auf: Jeder Capture trägt einen Zeitstempel, den MIME-Typ und HTTP-Status, den das Archiv gesehen hat, sowie einen Digest des Bodys. urx behält all das, sodass die CDX-gestützten Provider — wayback, cc, arquivo und jedes --cdx-endpoint — jede URL zusammen mit Folgendem melden:
Feld Bedeutung first_seenÄltester Capture-Zeitstempel, 14-stellige CDX-Form (YYYYMMDDhhmmss) last_seenNeuester Capture-Zeitstempel mimeMIME-Typ des jüngsten Captures, der einen aufgezeichnet hat archive_statusHTTP-Status, den das Archiv zum Zeitpunkt des Captures aufgezeichnet hat digestEin repräsentativer Inhalts-Digest über die Captures hinweg
archive_status ist nicht status: status erscheint nur unter --check-status, was die URL jetzt live erneut anfragt, wohingegen archive_status das ist, was der Crawler erhielt, als er die Seite erfasste. Eine URL kann durchaus archive_status 200 sein und heute tot sein.
Wenn dieselbe URL aus mehreren Captures oder mehreren Archiven stammt, werden die Werte zusammengeführt: first_seen ist der älteste Zeitstempel, den jemand gemeldet hat, last_seen der neueste, und mime/archive_status stammen aus dem jüngsten Capture, der sie hatte. Provider ohne Capture-Index (otx, vt, urlscan, zoomeye, github, bevigil, robots, sitemap und --files-Eingabe) melden nur die URL — für sie werden keine Werte erfunden.
Wie die Metadaten an die Oberfläche gelangen, hängt vom Format ab:
json / jsonl — jedes Feld erscheint als Schlüssel, wenn es einen Wert hat, und wird vollständig weggelassen, wenn nicht, genau wie sources.
csv — eine Spalte wird nur hinzugefügt, wenn mindestens eine Zeile einen Wert dafür hat, sodass ein Lauf ohne Metadaten weiterhin eine einzelne url-Spalte erzeugt.
- Klartext — standardmäßig unverändert, eine bloße URL pro Zeile, sodass bestehende Pipelines weiter funktionieren. Übergib
--show-meta, um die Felder anzuhängen.```bash
Rich records: when the URL was alive, and what it served
urx example.com --providers wayback -f jsonl
{"url":"https://example.com/old.php","first_seen":"20040112093000",
"last_seen":"20180722140311","mime":"text/html","archive_status":"200",
"digest":"HT2DYGA5UKZCPBSFVCV3JOBXGW2G5UUA"}
Triage by age: everything last captured before 2010
urx example.com -f jsonl | jq -r 'select(.last_seen < "20100101000000") | .url'
Opt plain output into the metadata
urx example.com --providers wayback --show-meta
Streaming (`--stream`) meldet nur URLs. Eine URL wird beim ersten Auftreten ausgegeben,
bevor die Captures eintreffen, die ihren `first_seen`/`last_seen`-Bereich erweitern würden,
daher wird `--show-meta` dort aus demselben Grund abgelehnt wie
`--show-sources`.
Ein Cache-Treffer trägt ebenfalls keine Metadaten: Der Cache speichert URLs, sodass eine Domain,
die aus dem Cache bedient wird, ihre URLs ohne Capture-Felder meldet. Verwenden Sie `--no-cache` (oder warten Sie
auf die TTL) für einen Lauf, der sie neu befüllt.
### Live-Response-Metadaten
`--check-status` sendet bereits eine Anfrage und wartet auf den Response-Head, sodass
das, was dieser Head trägt, kostenlos mitgeliefert wird: `Location`, `Content-Length` und
`Content-Type` werden zusammen mit dem Statuscode aufgezeichnet. Redirects werden weiterhin nie
verfolgt, sodass ein gemeldeter Status immer zu der URL gehört, die angefragt wurde, und
`location` einfach angibt, wohin der 3xx zeigte.
`--check-title` fügt den HTML-`<title>` hinzu. Es ist das einzige Feld, das nicht kostenlos ist –
ein Titel benötigt den Response-Body –, daher steht es hinter seinem eigenen Flag. Das Lesen wird
zweifach begrenzt (höchstens 64 KiB, und es stoppt am schließenden Tag) und für einen Body, den der
Server als Nicht-HTML deklariert hat, vollständig übersprungen, sodass eine JSON-API oder ein Bild
nichts kostet. Der Titel wird auf Leerraum reduziert, Entity-dekodiert und auf 200
Zeichen gekürzt. `--check-title` impliziert `--check-status`.```bash
urx example.com --check-status -f jsonl
urx example.com --check-title --show-meta
urx example.com --check-status --is 30x -f jsonl | jq -r '.url + " -> " + .location'
Die Offenlegung folgt der Regel, die die Archiv-Metadaten bereits festgelegt haben: json/jsonl/csv enthalten immer die Felder (fehlende Schlüssel werden weggelassen, und die CSV-Spalten werden nach den vorhandenen angehängt), während reiner Text eine bloße URL pro Zeile bleibt, es sei denn, --show-meta verlangt etwas anderes. In der reinen Textausgabe wird der Titel in Anführungszeichen gesetzt, da er der eine Wert ist, der regelmäßig Leerzeichen enthält.
Authentifizierte und benutzerdefinierte Anfragen
--check-status, --extract-links, --extract-js-endpoints und
--expand-specs fordern alle gesammelten URLs erneut vom Ziel selbst an. -H
gibt diesen Anfragen die Header, die sie benötigen:```bash
urx example.com --check-status -H "Authorization: Bearer $TOKEN"
urx example.com --extract-links --cookie "session=abc; role=admin"
urx example.com --check-status --user-agent "acme-security-scan/1.0"
`-H` ist wiederholbar, nimmt `Name: value` entgegen, und ein fehlerhaftes beendet den Lauf,
anstatt unbemerkt hinauszugehen — ein Argument, das stillschweigend verworfen wird, lässt
einen anonymen Scan als authentifizierten erscheinen. `--cookie` und
`--user-agent` sind Kurzformen für die entsprechenden Header.
**Diese Header erreichen niemals ein Archiv.** Sie werden nur von den Komponenten
gesendet, die mit dem Ziel kommunizieren: den vier oben genannten Testern sowie den
`robots`- und `sitemap`-Providern, die ebenfalls vom Ziel abrufen. Jeder andere Provider
fragt web.archive.org, index.commoncrawl.org oder eine Drittanbieter-API ab, und
`--archive-body` tut dies ebenfalls, wenn es eine Aufzeichnung wiedergibt; ihnen das
Session-Cookie des Ziels zu übergeben, würde eine Anmeldeinformation an einen Dienst
senden, der behält, was er empfängt, ohne Nutzen. Archivabfragen behalten urx' eigenen
User-Agent, den `--random-agent` weiterhin rotiert.
### Archivierte Antwort-Bodies durchsuchen
`--extract-links` ruft jede gesammelte URL von der Live-Seite ab, was genau der
falsche Ort ist, um nach den Seiten zu suchen, die bei einem OSINT-Sweep am meisten
zählen: diejenigen, die nicht mehr existieren. `--archive-body` ruft stattdessen die
Bodies ab, die die Wayback Machine gespeichert hat. Für jede gesammelte URL, die einen
Aufzeichnungs-Zeitstempel trägt, gibt urx diese Aufzeichnung in ihrer Rohform wieder
(`https://web.archive.org/web/<timestamp>id_/<url>` — das `id_`-Flag schaltet die
Wayback-Toolbar und die Link-Umschreibung ab, sodass der Body die Originalbytes sind) und
führt dieselbe Link-Extraktion darauf aus, die `--extract-links` verwendet.```bash
# Links from the archived bodies of everything the CDX providers found
urx example.com --archive-body
# Bound the run and pace it; the archive is one host no matter how many URLs
urx example.com --archive-body --archive-body-limit 200 --rate-limit 5
# Only the JavaScript those pages referenced back then
urx example.com --archive-body -e js
Warum dies weit weniger Anfragen als waymore erfordert. Jede CDX-Zeile trägt einen
Content-Digest, und zwei Captures mit demselben Digest sind byte-für-byte
derselbe Body. Archive sind voll davon: jede ?utm_source=-Variante einer Seite,
jede /index.html neben ihrem /, jede Tracking-Parameter-Permutation
liefert identische Bytes, sodass eine Liste mit Zehntausenden von URLs routinemäßig
auf ein paar Tausend verschiedene Bodies kollabiert. waymore hat keine Vorstellung davon — es
lädt eine Antwort pro URL herunter und bewältigt das Volumen durch eine stumpfe
-l 5000-Obergrenze, die sowohl das Archiv belastet als auch die Abdeckung abschneidet. urx
beansprucht jeden Digest beim ersten Auftreten und überspringt jede spätere URL, die
dieselben Bytes wiedergeben würde, sodass dieselbe Abdeckung eine Anfrage pro
verschiedenem Body kostet. --archive-body-limit (Standard 500) begrenzt verschiedene Bodies,
nicht URLs; Duplikate zählen nie dagegen, und --verbose berichtet, wie viele
übersprungen wurden.
Archiviertes JavaScript abbauen. Die API-Oberfläche einer modernen App lebt in ihren Bundles
als String-Literale, und --extract-js-endpoints holt diese von der Live-
Seite — wo sie häufig nicht mehr existieren. Bundles werden nach Build-Hash benannt, sodass
app.a3f9c2.js in dem Moment 404 liefert, in dem die Seite neu deployt wird, und die Endpunkte, die es nannte,
verschwinden mit ihm. Führe die beiden Flags zusammen aus und urx baut stattdessen die archivierte Kopie ab,
und die Inline-<script>-Blöcke einer archivierten Seite zusammen mit ihren Links:```bash
urx example.com --archive-body --extract-js-endpoints
**Die Bodies behalten.** Die Anfragen werden ohnehin bereits gestellt, sodass das Schreiben der Bodies auf die Festplatte nichts zusätzlich kostet und die Fragen beantwortet, die kein Link-Extraktor stellt: der `<!-- staging.internal -->`-Kommentar, das Token, das ein Build von 2019 inline eingebettet hat, der Stack-Trace, der eine Framework-Version nennt.```bash
urx example.com --archive-body --archive-body-dir ./corpus
grep -ri "api[_-]key" ./corpus
Jede Datei ist nach ihrer URL plus einem Hash davon benannt, und corpus/index.jsonl
ordnet jede Datei ihrer URL, dem Aufnahmezeitpunkt, dem Digest und dem Inhaltstyp zu.
Nur textähnliche Inhalte werden gespeichert — HTML, Skript, JSON, XML, CSS, reiner Text —
damit das Verzeichnis nicht mit den Bildern und Schriftarten der Website vollläuft. Da der
Abruf nach Digest dedupliziert wird, deckt das Korpus pro Anfrage weit mehr des Ziels ab,
als es eine Antwort pro URL tun würde.
Wissenswerte Details:
- Nur URLs mit einem Aufnahmezeitpunkt qualifizieren sich. Die CDX-Anbieter (
wayback,
cc, arquivo) liefern einen; --files-Eingabe, Nicht-CDX-Anbieter und
zwischengespeicherte Ergebnisse (der Cache speichert nur URLs) haben keinen. urx sagt
es, wenn es nichts zum Wiedergeben gibt — übergib --no-cache, um frische Aufnahmen zu
erhalten.
- Die neueste Aufnahme jeder URL wird wiedergegeben. Ein von einem anderen Archiv
gemeldeter Zeitstempel landet auf der nächstgelegenen Wayback-Aufnahme; eine URL, die
die Wayback Machine nie gesehen hat, antwortet mit 404 und wird übersprungen. Aufnahmen,
die das Archiv als Fehler verzeichnet hat, werden nicht ausgewertet, genau wie
--extract-links Live-Fehlerseiten ignoriert.
- Entdeckte Links durchlaufen dieselben Filter, Host-Validierung und
Ausgabe-Transformationen wie alles andere, und jeder Inhalt ist auf 10 MiB begrenzt.
--rate-limit, --rate-limit-by wayback=N, --parallel, --proxy,
--timeout und --retries gelten alle für die Wiedergabeanfragen.
- Inkompatibel mit
--stream, wie jede Option, die nach der Sammlung ausgeführt wird.
Erweitern von API-Spezifikationen
Ein -p only-api-Durchlauf findet /swagger.json, /openapi.yaml und /v3/api-docs
und öffnet sie dann nie: --extract-links parst HTML, --extract-js-endpoints
verwirft application/json-Inhalte, und --archive-body führt den HTML-Parser über
das aus, was das Archiv zurückgibt. --expand-specs liest sie und erweitert jede Route,
die sie beschreiben, in die Ergebnismenge — eine Anfrage kauft die gesamte dokumentierte
Oberfläche, exakt und bereits parametrisiert.```bash
urx example.com -p only-api --expand-specs
urx example.com --expand-specs --max-spec-files 10 --rate-limit 2
Recover an API the live host no longer serves: read the archived document
urx example.com --archive-body --expand-specs
Was erweitert wird:
* **OpenAPI 3.x** — `servers[].url` (absolut, dokumentrelativ und templatisiert,
wobei `{var}` aus `variables[var].default` oder dem ersten `enum`-Wert
aufgelöst wird) gekreuzt mit jedem `paths`-Schlüssel; die eigenen `servers`
eines Path Items überschreiben die des Dokuments.
* **Swagger 2.0** — `schemes` × `host` + `basePath`, wobei jeder Teil auf den
entsprechenden Teil der URL des Dokuments selbst zurückfällt. `ws`/`wss`
werden verworfen.
* **GraphQL-Introspektion** — eine URL pro Query-, Mutation- und
Subscription-Feld, geschrieben als der Endpunkt plus `?query=…`. Ein als
Datei gespeichertes Schema wird zu seinem Endpunkt aufgelöst
(`/graphql/schema.json` → `/graphql`).
JSON und YAML werden beide gelesen. Ziele werden zuerst nach Namen und
kostenlos gewählt (ein Spec-Marker-Substring — `swagger`, `openapi`,
`api-docs`, `graphql`, `introspection` — plus eine `json`/`yaml`/`yml`-Erweiterung,
falls vorhanden, sodass `swagger-ui.html` keine Anfrage kostet), dann nach dem
`Content-Type` der Antwort. Pfadtemplates werden so ausgegeben, wie das Dokument
sie schreibt (`/users/{id}`, nicht `/users/%7Bid%7D`). Bodies sind auf 10 MiB
begrenzt, und ein YAML-Dokument mit mehr als 32 Alias-Referenzen wird vor dem
Parsen abgelehnt, um Expansion Bombs auszuschließen. `--max-spec-files`
(Standard 50) begrenzt die abgerufenen Dokumente. Mit ebenfalls aktiviertem
`--archive-body` wird eine archivierte Spezifikation ohne zusätzliche
Anfragekosten als eine gelesen — der Body wurde ohnehin bereits abgerufen.
### Archivierte robots.txt und sitemap.xml
Die Provider `robots` und `sitemap` lesen die *Live*-Dateien, die nur aussagen,
was eine Website heute verbirgt oder auflistet. `--archived-discovery` liest
zusätzlich jede unterschiedliche Version dieser Dateien, die die Wayback Machine
gespeichert hat. Ein `Disallow:` aus dem Jahr 2015 benennt Pfade, die die Website
seither nicht mehr erwähnt — oft, weil sie vergessen werden sollten, nicht weil
sie verschwunden sind — und eine alte Sitemap listet alles auf, was die Website
einst gecrawlt haben wollte.```bash
# Every archived version of robots.txt and sitemap.xml, alongside the live ones
urx example.com --archived-discovery
# Bound it and pace it; both archived providers answer to --rate-limit-by
urx example.com --archived-discovery --archived-discovery-limit 20 --rate-limit-by robots=2,sitemap=2
# Only the versions captured in a given era
urx example.com --archived-discovery --from 2014 --to 2016
So funktioniert es und warum es günstig ist:
- Die Versionen eines Dokuments werden mit einer CDX-Abfrage pro Datei
aufgelistet (
robots.txt, sitemap.xml, sitemap_index.xml, sitemap.txt),
wobei collapse=digest verwendet wird, sodass aufeinanderfolgende Captures,
die dieselben Bytes ausgeliefert haben, zu einer Zeile zusammengefasst werden.
Es werden nur Zeilen abgefragt, die als Erfolg verzeichnet sind: Der Index
fasst www. und die Apex-Domain zu einem Eintrag zusammen, und ihre
verschachtelten 301/200-Zeilen würden andernfalls das Collapse
vereiteln — für github.com/robots.txt sind das 325k Zeilen ohne den Filter
und 14k mit ihm, für dieselben 107 verschiedenen Versionen.
- Jede verschiedene Version wird in Rohform (
/web/<timestamp>id_/…)
wiedergegeben und dem selben Parser wie die Live-Datei übergeben. Kein
zweiter Parser: Eine robots.txt von 2015 wird nach genau denselben Regeln
gelesen wie die aktuelle, einschließlich der Guards für absolute Pfade und
übersprungene Muster. Ein archivierter <sitemapindex> wird in seine
Kindelemente verfolgt, wie sie zu jenem Zeitpunkt waren.
- Captures, die das Archiv als Fehler verzeichnet hat (github.coms robots.txt
war einen Teil des Jahres 2007 über ein 401), werden ohne Anfrage
übersprungen und nur unter
--verbose gemeldet.
--archived-discovery-limit (Standard 50) begrenzt die pro Domain von jedem
archivierten Provider abgerufenen Dokumente, neueste Versionen zuerst;
verschachtelte Sitemaps zählen mit. --verbose gibt an, wann das Limit die
Liste verkürzt hat.
- Die archivierten Varianten laufen als eigene Provider-Instanzen — „Robots.txt
(archived)" und „Sitemap (archived)" in
--stats und --show-sources — aber
unter den bestehenden IDs robots / sitemap, sodass --exclude-robots,
--exclude-sitemap und --rate-limit-by robots=N sowohl die Live- als auch
die archivierten Lesevorgänge steuern. --from / --to schränken ein,
welche Versionen berücksichtigt werden.
- Funktioniert mit
--stream; es ist ein Provider wie jeder andere.
Filterung auf der Archivseite
--archive-status, --archive-mime, --from und --to werden vom
CDX-Index des Archivs ausgewertet und nicht von urx. Zwei Konsequenzen sind
wissenswert:
- Sie gelten nur für CDX-gestützte Provider —
wayback, cc, arquivo und
jedes --cdx-endpoint. Andere Provider ignorieren sie; urx warnt, wenn
keiner aktiviert ist.
- Die Archive teilen sich nicht einen Filterdialekt. Wayback Machine (und
jeder
--cdx-dialect classic-Endpunkt) behandelt Werte als reguläre
Ausdrücke, sodass --archive-status "30." auf jedes 3xx passt. Common
Crawl, Arquivo.pt und pywb-Endpunkte stimmen exakt überein, und ihr Index
verknüpft wiederholte Filter mit AND — eine mehrwertige Positivliste wie
--archive-status 200,301 ist dort also unerfüllbar. urx überspringt diesen
Filter für jene Provider (mit einer Warnung), anstatt eine Abfrage zu senden,
die leer zurückkäme. Mehrwertige Ausschlüsse bedeuten „nicht dies und nicht
das" und funktionieren überall.
Verwenden Sie --archive-status, wenn Sie das wollen, was das Archiv zum
Crawl-Zeitpunkt verzeichnet hat, und --check-status / --include-status,
wenn Sie den Status des Ziels jetzt wollen; Letzteres fragt jede URL erneut
ab.
Benutzerdefinierte CDX-Endpunkte
Jedes Webarchiv, das auf pywb, OutbackCDX oder dem CDX-Server des Internet
Archive aufbaut, stellt dieselbe Abfrage-API bereit. Anstatt einen Provider pro
Archiv fest zu codieren, macht --cdx-endpoint URL jeden solchen Server
sofort zu einem Provider:```bash
The Icelandic web archive, alongside the default providers
urx example.is --cdx-endpoint https://vefsafn.is/cdx
Several at once; each gets its own progress line, stats row and rate limit
urx example.com --cdx-endpoint https://vefsafn.is/cdx --cdx-endpoint http://localhost:8080/cdx
--rate-limit-by cdx:vefsafn.is=1
* Die Provider-ID ist `cdx:<host>` (`cdx:vefsafn.is`), die von
`--exclude-providers`, `--rate-limit-by`, `--stats` und `--show-sources` verwendet wird.
Das Benennen eines Endpunkts aktiviert ihn; es ist kein `--providers`-Eintrag erforderlich, und
`--providers cdx:vefsafn.is` führt ihn allein aus. `--list-providers` zeigt die
Endpunkte an, die in derselben Befehlszeile benannt sind, mit den IDs, unter denen sie ausgeführt werden.
* Alles, was die integrierten CDX-Provider berücksichtigen, gilt auch hier: `--subs`,
`--from`/`--to`, die `--archive-*`-Filter, Paginierung, `--rate-limit` und
die oben beschriebenen Capture-Metadaten.
* `--cdx-dialect classic|pywb` benennt den Dialekt des Servers (Feldnamen, Filter-
Semantik, Zeilenformat und Paginierungsschema folgen alle daraus — siehe
„Archive-side Filtering"). Bleibt es ungesetzt, sondiert urx den Endpunkt einmal pro Lauf
und fällt auf `pywb` zurück, den häufigeren Dialekt; setzen Sie es explizit, wenn die
Sondierung es nicht erkennen kann (z. B. eine leere Antwort für eine unbekannte Domain).
* Kann auch in der Konfigurationsdatei gesetzt werden (`cdx_endpoint = [...]`, `cdx_dialect`).
**Verifizierte Endpunkte.** Zum Zeitpunkt dieses Schreibens ist der einzige öffentliche Endpunkt, der
bestätigt von Anfang bis Ende funktioniert, `https://vefsafn.is/cdx` (Landsbókasafns isländisches Web-
Archiv, pywb-Dialekt). Zwei Dinge sind darüber zu wissen: Er ignoriert `limit`, `page`
und `showNumPages` und gibt für jede Abfrage die vollständige Ergebnismenge zurück, was
urx handhabt; und nach einer Handvoll Anfragen kann er beginnen, mit einer
Bot-Schutzseite im Anubis-Stil zu antworten („Session Verification"). urx erkennt eine HTML-
Antwort anstelle von CDX-Zeilen und meldet sie als Provider-Fehler unter Nennung des
Endpunkts — sie wird niemals als „no URLs" gezählt. Wenn Sie darauf stoßen, verlangsamen Sie mit
`--rate-limit-by cdx:vefsafn.is=1` oder versuchen Sie es später erneut.
**Bekanntlich nicht funktionierend.** Das UK Web Archive (`webarchive.org.uk`), das Webarchiv der Library of
Congress (`webarchive.loc.gov`), die Bibliotheca Alexandrina und die
National Library of Australia (`web.archive.org.au`) befinden sich alle hinter Bot-
Schutz oder Weiterleitungen, die ihre CDX-APIs von einem Kommandozeilen-Client aus blockieren.
urx versucht nicht, das zu umgehen, sodass das Richten von `--cdx-endpoint` auf sie den
oben genannten HTML-Antwort-Fehler ergibt.
### Caching und inkrementelles Scannen
Urx unterstützt Caching, um die Leistung bei wiederholten Scans zu verbessern, sowie inkrementelles Scannen, um nur neue URLs zu entdecken.```bash
# Enable caching with SQLite (default)
urx example.com --cache-type sqlite --cache-path ~/.urx/cache.db
# Use Redis for distributed caching
urx example.com --cache-type redis --redis-url redis://localhost:6379
# Incremental scanning - only show new URLs since last scan
urx example.com --incremental
# Set cache TTL (time-to-live) to 12 hours
urx example.com --cache-ttl 43200
# Disable caching entirely
urx example.com --no-cache
# Combine incremental scanning with filters
urx example.com --incremental -e js,php --patterns api
# Configuration file with caching settings
urx -c example/config.toml example.com
Cache-Verwaltung
urx cache untersucht und pflegt den Cache, ohne die Datenbank von Hand
anzufassen. Jeder Unterbefehl berücksichtigt dieselben --cache-type, --cache-path,
--redis-url und --cache-ttl wie ein Scan, und alle fünf funktionieren mit beiden
Backends.```bash
urx cache stats # entries, domains, URLs, age span, size, expired count
urx cache list # per-domain counts, last scan, TTL remaining
urx cache list --domain '*.example.com'
urx cache prune # delete only what --cache-ttl has expired
urx cache drop example.com # rescan one target without clearing the rest
urx cache clear --yes # delete everything
machine-readable
urx cache stats -f json | jq '.expired_entries'
Domain-Abgleich ist unabhängig von Groß-/Kleinschreibung und **exakt**, es sei denn, das Muster enthält
`*` — ein Teilstring-Standard hätte zugelassen, dass `drop example.com` auch
`notexample.com` entfernt. `clear` fragt vor dem Löschen nach und verweigert
eine nicht-interaktive stdin, anstatt eine Antwort anzunehmen, `drop` benennt jedes Muster,
das nichts getroffen hat, das Betrachten des Caches erstellt nie einen, und Redis wird
mit `SCAN` durchsucht statt mit dem blockierenden `KEYS` (wobei jedes Passwort in `--redis-url`
vor der Ausgabe redigiert wird).
#### Caching-Anwendungsfälle```bash
# Daily monitoring - only alert on new URLs (built-in webhook, see below)
urx target.com --incremental --silent --notify https://hooks.slack.com/services/... --notify-format slack
# ...or hand the new URLs to an external notifier
urx target.com --incremental --silent | notify-tool
# Efficient domain lists processing
cat domains.txt | urx --incremental --cache-ttl 3600 > new_urls.txt
# Distributed team scanning with Redis
urx example.com --cache-type redis --redis-url redis://shared-cache:6379
# Fast re-scans during development
urx test-domain.com --cache-ttl 300 # 5-minute cache for rapid iterations
Webhook-Benachrichtigungen
--notify <URL> sendet eine Zusammenfassung des Durchlaufs per POST an einen Webhook, wenn der Durchlauf endet,
wodurch --incremental zu einem Monitor wird: In cron eingetragen, wird der Webhook
nur ausgelöst, wenn etwas Neues auftaucht.```bash
Slack incoming webhook, only when the run finds new URLs (the default)
urx target.com --incremental --silent
--notify https://hooks.slack.com/services/T000/B000/XXXX --notify-format slack
Discord, and send even when nothing is new
urx target.com --incremental --notify "$DISCORD_HOOK" --notify-format discord --notify-on always
Several receivers, urx's own JSON schema (the default format)
urx target.com --incremental --notify https://n8n.example/hook --notify https://ntfy.example/urx
Keep the webhook out of the shell history
export URX_NOTIFY_URL=https://hooks.slack.com/services/...
urx target.com --incremental --notify-format slack
- `--notify-on` ist standardmäßig `new`: Es wird nichts gesendet, wenn der Lauf null
URLs ausgibt, sodass ein ruhiger Cron-Lauf ruhig bleibt. `always` sendet unabhängig davon; `never`
behält die Konfiguration bei, deaktiviert aber das Senden.
- `--notify-format json` (Standard) sendet das Schema von urx: `domains`,
`incremental`, `url_count`, `new_url_count`, `elapsed_ms`, eine `providers`-Liste
pro Provider (dieselben Zahlen, die `--stats` ausgibt), und ein `sample` von bis zu
20 ausgegebenen URLs mit gesetztem `sample_truncated`, wenn mehr gefunden wurden.
`slack` sendet `{"text": ...}` und `discord` sendet `{"content": ...}` mit einer
kurzen, für Menschen lesbaren Nachricht; Nachrichten, die länger sind als der Dienst erlaubt, werden
an einer Zeilengrenze abgeschnitten und enden mit `[truncated: N lines cut ...]`.
- Die Zustellung ändert niemals den Exit-Code. Die URLs stehen bereits auf stdout oder in
`--output`, wenn der Webhook aufgerufen wird, sodass ein toter Webhook eine Warnung
auf stderr ist und der Lauf trotzdem mit 0 endet. `--verbose` zeigt den Antwortstatus.
- Die Webhook-URL ist ein Credential. urx gibt niemals mehr als ihr Schema und
ihren Host aus — nicht in `--verbose`, nicht in Warnungen, nicht in `--stats`. Um sie aus
einer eingecheckten Konfiguration herauszuhalten, lege sie in `URX_NOTIFY_URL` oder als
`notify_url` in der Provider-Konfigurationsdatei ab; `[notify].url` in der Hauptkonfiguration
funktioniert ebenfalls. Die Priorität ist CLI/Env > Provider-Konfiguration > Hauptkonfiguration.
- Die Anfrage berücksichtigt `--proxy`, `--proxy-auth`, `--timeout` und `--insecure`.
`--network-scope` gilt nicht: Es partitioniert den Verkehr, der auf das Ziel
und die Archive gerichtet ist, und der Webhook ist dein eigener Endpunkt.
- `--silent` sendet trotzdem (das ist der Hauptanwendungsfall); es verbirgt nur die
Diagnosen.
## Integration mit anderen Tools
Urx funktioniert gut in Pipelines mit anderen Sicherheits- und Reconnaissance-Tools:```bash
# Find domains, then discover URLs
echo "example.com" | urx | grep "login" > potential_targets.txt
# Combine with other tools
cat domains.txt | urx --patterns api | other-tool
Inspiration
Urx wurde von gau (GetAllUrls) inspiriert, einem Tool, das bekannte URLs von AlienVaults Open Threat Exchange, der Wayback Machine und Common Crawl abruft. Obwohl es ähnliche Kernfunktionen bietet, wurde Urx von Grund auf in Rust entwickelt, mit Fokus auf Leistung, Nebenläufigkeit und erweiterte Filterfunktionen.
Mitwirken
Urx ist ein Open-Source-Projekt und wurde mit ❤️ erstellt.
Wenn du zu diesem Projekt beitragen möchtest, lies bitte CONTRIBUTING.md und reiche einen Pull-Request mit deinen coolen Inhalten ein.