
urx v0.11.0
Extrae URLs de archivos OSINT para obtener información de seguridad.
Extrae URLs de archivos OSINT para obtener información de seguridad.
Urx es una herramienta de línea de comandos diseñada para recopilar URLs de archivos OSINT, como Wayback Machine y Common Crawl. Construida con Rust para mayor eficiencia, aprovecha el procesamiento asíncrono para consultar rápidamente múltiples fuentes de datos. Esta herramienta simplifica el proceso de recopilación de información de URLs para un dominio específico, proporcionando un conjunto de datos completo que puede utilizarse para diversos fines, incluidos las pruebas de seguridad y el análisis.
Características
- Obtiene URLs de múltiples fuentes en paralelo (Wayback Machine, Common Crawl, OTX, Arquivo.pt)
- Conecta cualquier otro servidor de índice CDX — archivos web nacionales, un pywb privado, OutbackCDX — con
--cdx-endpoint URL, sin necesidad de cambiar el código - Sin claves por defecto: Wayback, Common Crawl, OTX, Arquivo.pt y URLScan (anónimo) funcionan sin una clave de API
- Proveedor BeVigil: URLs extraídas de aplicaciones Android desempaquetadas — endpoints que ningún archivo web llegó a rastrear
- Soporte de rotación de claves de API para los proveedores VirusTotal y URLScan para mitigar los límites de velocidad
- Pruebas autenticadas:
-H,--cookiey--user-agentse aplican a cada solicitud que urx realiza al objetivo (--check-status,--extract-links,--extract-js-endpoints,--expand-specs) y deliberadamente nunca se envían a un archivo - Filtra resultados por extensiones de archivo, patrones de subcadenas o expresiones regulares completas (
--match-regex/--filter-regex) - Presets predefinidos, tanto por familia de archivos ("no-images", "only-js") como por interés de seguridad ("only-secrets", "only-backup", "only-config", "only-api")
- Filtrado del lado del archivo: envía el código de estado, el tipo MIME y el rango de fechas directamente a la consulta CDX, de modo que las capturas filtradas nunca cruzan la red
- Filtrado de metadatos del lado del cliente (
--meta-*): filtra por fecha de primera/última captura, tipo MIME registrado y estado registrado de forma uniforme en todos los proveedores, tras la recopilación - Objetivos con alcance de ruta:
urx example.com/shopenvía el alcance directamente a la consulta CDX (url=example.com/shop*), por lo que un subárbol de un sitio grande cuesta una fracción del índice completo en lugar de filtrarse del lado del cliente - Archivos de alcance de bug-bounty (
--scope-file): la propia lista*.example.com/!admin.example.comde un programa utilizada textualmente, repetible y unida, con las exclusiones siempre prevaleciendo - Normalización y deduplicación de URLs: ordena los parámetros de consulta, elimina las barras finales, fusiona URLs semánticamente idénticas y colapsa casi-duplicados que difieren solo en ids, hashes o fechas (
--dedup-similar) - Soporte para múltiples formatos de salida: texto plano, JSON, JSON Lines, CSV y
wordlist— los segmentos de ruta y nombres de parámetros con los que se construye el objetivo, dejando fuera ids, hashes y fechas - Vistas de parámetros y fuzzing:
--params(el inventario completo de parámetros del objetivo),--params-by-endpoint(qué endpoint acepta qué) y--fuzz-placeholder FUZZ(una URL con plantilla por firma de parámetro, lista para ffuf o dalfox) - Metadatos de captura del archivo:
first_seen,last_seen,mime,archive_statusydigestse devuelven con cada URL que un archivo CDX reportó, sin coste de red adicional - Salida en streaming (
--stream): las URLs se escriben a medida que cada proveedor las reporta, por lo que un pipeline empieza a funcionar de inmediato en lugar de esperar al archivo más lento - Soporte de entrada directa de archivos: lee URLs directamente desde archivos WARC, archivos comprimidos de URLTeam y archivos de texto
- Envía los resultados a la consola o a un archivo, o transmítelos por stdin para la integración en pipelines
- Pruebas de URLs:
- Filtra y valida URLs según códigos de estado HTTP y patrones.
- Extrae enlaces adicionales de las URLs recopiladas — anclas, scripts, hojas de estilo, acciones de formularios, iframes, imágenes, fuentes multimedia, objetos, embeds y objetivos de meta-refresh
- Mina los cuerpos de respuesta archivados de las URLs recopiladas (
--archive-body), de modo que las páginas que ya no existen siguen entregando los enlaces que contenían — una solicitud por cuerpo distinto, gracias a la deduplicación por digest de CDX - Con
--extract-js-endpoints, mina también el JavaScript archivado: un bundle nombrado por hash de compilación devuelve 404 en el momento en que el sitio se redespliega, y el archivo es el único lugar donde su superficie de API sigue existiendo - Conserva los cuerpos reproducidos (
--archive-body-dir) como un corpus para buscar con grep lo que ningún extractor de enlaces busca — comentarios de desarrolladores, credenciales incrustadas, nombres de host internos — sin solicitudes adicionales - Expande especificaciones de API (
--expand-specs): documentos OpenAPI 3.x, Swagger 2.0 e introspección de GraphQL, en JSON o YAML, convertidos en cada ruta que describen — una solicitud compra toda la superficie documentada - Metadatos de respuesta:
--check-statustambién registraLocation,Content-LengthyContent-Type, y--check-titleañade el<title>HTML
- Descubrimiento de robots.txt y sitemap.xml archivados (
--archived-discovery): cada versión distinta que conserva Wayback Machine, de modo que unDisallow:de 2015 sigue nombrando las rutas que el sitio ha dejado de mencionar desde entonces - Caché y escaneo incremental:
- Caché local en SQLite o remota en Redis para evitar reescanear dominios
- Modo incremental para descubrir solo URLs nuevas desde el último escaneo
- TTL de caché configurable y limpieza automática de entradas caducadas
- Subcomando
urx cachepara inspeccionar y mantener la caché:stats,list,prune,drop <domain>,clear

Instalación
Desde Cargo```bash
https://crates.io/crates/urx
cargo install urx
### Desde Homebrew```bash
# https://formulae.brew.sh/formula/urx
brew install urx
Desde el código fuente```bash
git clone https://github.com/hahwul/urx.git cd urx cargo build --release
El binario compilado estará disponible en `target/release/urx`.
### Desde Docker
[ghcr.io/hahwul/urx](https://github.com/hahwul/urx/pkgs/container/urx)
### Autocompletado de Shell
`urx` genera su propio script de autocompletado, por lo que siempre coincide con las flags del
binario que realmente tienes instalado.```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 y elvish también son compatibles. La opción no necesita un dominio objetivo.
Página del manual```bash
urx --manpage > ~/.local/share/man/man1/urx.1 man urx
## Uso
### Uso básico```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
Opciones```
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` lee todas las etiquetas que contienen URL, no solo los anclajes: `<a href>`,
`<script src>`, `<link href>`, `<form action>`, ``, ``,
`<source src>`, `<object data>`, `<embed src>` y los destinos de `<meta http-equiv="refresh">`.
Las URL relativas se resuelven contra la página (respetando `<base href>`),
los duplicados se eliminan y los enlaces descubiertos pasan por los mismos filtros
y validación de host que el resto de la ejecución. Consulta
[docs/content/guide/cli-options.md](https://github.com/hahwul/urx/blob/main/docs/content/guide/cli-options.md) para ver la
tabla completa.
`--extract-js-endpoints` va un paso más allá y lee el propio JavaScript:
cada URL recopilada que parece un script se descarga y sus
literales de cadena se examinan en busca de las rutas y URL que llama la aplicación:
`fetch("/api/v2/users")`, `axios.post("/graphql")`, el prefijo estático de
`` `/api/orders/${id}` ``. Estos son los endpoints que nunca aparecen en el HTML.
La salida se desruidiza de forma agresiva (se descartan tipos MIME, especificadores de módulo, base64,
valores CSS, fragmentos de regex y más), cada cuerpo se limita a
10 MiB, el número de archivos descargados está acotado por `--max-js-files`, y los
endpoints descubiertos pasan por los mismos filtros y validación de host que todo lo
demás. La política completa de extracción y supresión de ruido está en
[docs/content/guide/cli-options.md](https://github.com/hahwul/urx/blob/main/docs/content/guide/cli-options.md#javascript-endpoint-extraction).
`--archive-body` realiza la misma extracción sobre los cuerpos que la Wayback Machine
*almacenó* en lugar de sobre el sitio en vivo, de modo que una página que se eliminó hace años
sigue proporcionando los enlaces que contenía. Consulta
[Mining Archived Response Bodies](#mining-archived-response-bodies).
### Ejemplos```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
Delimitar una ejecución a una ruta
Un objetivo puede nombrar una ruta, y significa lo que dice: urx example.com/shop
recopila la parte del sitio bajo /shop.```bash
urx example.com/shop
urx https://example.com/api/v2 # a pasted URL works too
Esto no es un filtro aplicado a posteriori. Un índice CDX responde consultas de prefijo de forma nativa, así que urx envía `url=example.com/shop*` y el archivo nunca transfiere el resto del sitio por la red — en un objetivo grande esa es la diferencia entre unos cientos de filas y unos cientos de miles. A los proveedores que no pueden expresar una ruta en su consulta (OTX, VirusTotal, urlscan, GitHub, BeVigil, ZoomEye) se les pregunta por el host y sus respuestas se acotan después, al igual que los resultados de una ejecución con `--subs`, donde la forma `*.host` y un prefijo de ruta no pueden combinarse en una sola consulta CDX.
El alcance significa *en o bajo* la ruta: `/shop` y `/shop/cart` están dentro, `/shopping` no. Las mayúsculas y minúsculas se ignoran, porque un servidor CDX convierte toda la URL a minúsculas cuando construye la clave de su índice — `example.com/Shop*` y `example.com/shop*` devuelven las mismas filas, todas escritas en minúsculas, así que una comprobación sensible a mayúsculas descartaría todo lo que el archivo acababa de devolver. Una cadena de consulta o fragmento en el objetivo se descarta — esos acotan una petición, no un alcance.
> Nota: urx solía descartar la ruta de un objetivo, así que
> `urx https://example.com/shop` escaneaba todo `example.com`. Ahora
> escanea `/shop`. Pasa solo el host para el comportamiento antiguo; una ejecución cuyo objetivo
> lleva una ruta lo indica en stderr.
### Filtrado por expresiones regulares
`--patterns` / `--exclude-patterns` son simples comprobaciones de subcadenas: ambos lados se convierten a minúsculas, y cada metacaracter es literal. `--match-regex` / `--filter-regex` son las contrapartes de expresiones regulares, y difieren en tres aspectos que vale la pena recordar:
| | `--patterns` | `--match-regex` |
|---|---|---|
| Coincidencia | subcadena | [sintaxis regex](https://docs.rs/regex/latest/regex/#syntax) completa |
| Mayúsculas/minúsculas | insensible (ambos lados a minúsculas) | **sensible** — usa `(?i)` para desactivarlo |
| Múltiples valores | una bandera separada por comas | repite la bandera; las comas nunca se dividen |
Ambas banderas de regex se evalúan contra la **cadena de URL completa** tal como se recopiló (esquema, host, ruta y consulta), así que tanto `^https://` como `\.js$` funcionan. La exclusión gana: una URL que coincide con `--filter-regex` se descarta aunque `--match-regex` también haya coincidido. Una expresión mal formada hace fallar la ejecución al inicio, antes de que se consulte ningún archivo.
### Archivos de alcance
El alcance de un programa de bug bounty es una lista de hosts, y todas las plataformas la escriben de la misma manera. `--scope-file` toma esa lista tal cual en lugar de obligarte a traducirla a mano en alternancias de regex ancladas — donde equivocarse en el anclaje *amplía* silenciosamente el alcance en lugar de fallar.```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
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |```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` coincide con el dominio raíz y con todo lo que hay debajo (la lectura de bug-bounty, que es lo que significa la tabla de alcance de una plataforma); un host sin comodines coincide exactamente con ese host; un `*` solo convierte el archivo en una lista de denegación pura; las exclusiones siempre ganan; `#` inicia un comentario. Cualquier cosa que urx no pueda respetar — un puerto, una ruta, un comodín en medio — es un error de inicio que nombra el archivo y la línea en lugar de un alcance silenciosamente más amplio. El filtro se aplica a todos los proveedores y a los enlaces extraídos, y se combina con `--strict` en lugar de reemplazarlo, por lo que una línea de alcance `*.example.com` todavía necesita `--subs`.
### Filtros de metadatos de archivo
`--from`/`--to` y los predicados `--archive-*` se insertan en la consulta propia del archivo, lo que los hace gratuitos y también los limita a proveedores respaldados por CDX — y los dos dialectos de CDX discrepan lo suficiente como para que una lista positiva de múltiples valores (`--archive-status 200,301`) sea insatisfacible en servidores pywb. Los ocho filtros `--meta-*` se ejecutan *después* de la recolección, sobre un conjunto fusionado de metadatos de captura por URL, por lo que se aplican a todos los proveedores de manera uniforme.```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
Las URLs que no llevan metadatos — los proveedores que no son CDX, la entrada --files, los aciertos de caché — se dividen según la dirección del predicado: un predicado positivo no puede satisfacerse con un valor ausente, por lo que la URL se descarta; una exclusión solo descarta lo que coincide positivamente, por lo que sobrevive. --verbose informa de la división, y cuando los metadatos ausentes explican todo el conjunto de resultados, urx lo indica incluso sin -v, porque un acierto de caché haría que una ejecución vacía pareciera un objetivo sin nada que encontrar.
Colapsar casi duplicados
Un archivo devolverá sin problema /post/1 hasta /post/99999. Son un solo endpoint, y --dedup-similar imprime una línea para ellos. Un segmento de ruta se trata como dato — no como parte de la ruta — cuando es enteramente uno de los siguientes:
- una secuencia de dígitos (
/post/1, /page/42)
- un UUID (
/u/550e8400-e29b-41d4-a716-446655440000)
- un digest hexadecimal de 32/40/64 caracteres (md5, sha1, sha256)
- una fecha separada (
/blog/2024-01-02/)
- un token largo de mayúsculas y minúsculas mezcladas con dígitos (ids de sesión, blobs firmados)
Los segmentos que meramente contienen dígitos se mantienen, por lo que /api/v1/ y /api/v2/ siguen siendo dos endpoints, y un slug en minúsculas es prosa en lugar de un token. Las cadenas de consulta se agrupan solo por nombres de parámetros: ?q=cats&page=1 y ?q=dogs&page=7 se colapsan, mientras que ?q=cats por sí solo no — eliminar un parámetro cambia la petición.
El superviviente de cada grupo es su URL lexicográficamente más pequeña, por lo que dos ejecuciones sobre los mismos datos imprimen lo mismo. --verbose informa de cuántas URLs se colapsaron. La opción es independiente de --normalize-url y --merge-endpoint y se combina con cualquiera de ellas; las tres necesitan el conjunto de resultados completo, por lo que ninguna funciona con --stream.
Vistas de parámetros y fuzz
--show-only-param solo corta la cadena de consulta de cada URL, lo que no puede responder a la primera pregunta que hace un tester: ¿qué parámetros acepta este objetivo? Tres vistas lo responden, construidas sobre la misma agrupación que usa --dedup-similar.```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` colapsa los segmentos de ruta que parecen id a `{id}` exactamente como
lo hace `--dedup-similar`, y escribe el endpoint completo porque urx
escanea habitualmente varios hosts en una sola ejecución. `--fuzz-placeholder` mantiene una URL por
firma de parámetro y conserva su ruta real — un `{id}` no enrutaría — por lo que
la salida alimenta directamente a un fuzzer:```bash
urx example.com --fuzz-placeholder FUZZ | ffuf -w - -u FUZZ
urx example.com --fuzz-placeholder FUZZ | dalfox pipe
Los tres necesitan el conjunto de resultados completo, por lo que son solo por lotes y mutuamente excluyentes entre sí y con las vistas --show-only-*.
Salida de lista de palabras
-f wordlist convierte una ejecución en una lista de palabras específica del objetivo: cada segmento de ruta y nombre de parámetro de consulta que vio, deduplicado en toda la ejecución y ordenado, un término por línea.```bash
urx example.com --subs -f wordlist -o words.txt
ffuf -w words.txt -u https://example.com/FUZZ
Los segmentos que parecen datos en lugar de nombres de ruta se omiten, reutilizando las agrupaciones de la prueba `--dedup-similar` — una wordlist llena de `4711`, UUIDs, fechas y tokens de sesión es peor que no tener wordlist, ya que cada una de esas palabras existe en exactamente un objetivo. Un segmento cuyo stem es un identificador también se descarta (`article-1234.html`). Se preserva el uso de mayúsculas y minúsculas: los segmentos de ruta distinguen mayúsculas de minúsculas en la mayoría de los orígenes, por lo que convertir `WebResource.axd` a minúsculas produciría una palabra que devuelve 404 en todos los lugares donde se pruebe. La unión debe tomarse sobre el conjunto completo, por lo que el formato es solo por lotes.
### Salida en streaming
Por defecto, urx recopila todo, luego filtra, ordena e imprime una sola vez. En un objetivo grande eso significa que no hay salida alguna hasta que finaliza el archivo más lento. `--stream` escribe cada URL en el momento en que el proveedor que la reporta responde:```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'
Las URL transmitidas en streaming pasan exactamente los mismos filtros que una ejecución por lotes y siguen deduplicándose. Dos cosas difieren:
- Orden. Los resultados llegan en el orden de finalización de los proveedores, por lo que la salida no está ordenada. Canaliza a través de
sort si necesitas ordenación.
- Alcance. Las opciones que necesitan el conjunto completo de resultados se rechazan de antemano (con un mensaje que nombra cada una):
--merge-endpoint, --dedup-similar, --check-status /
--include-status / --exclude-status, --extract-links,
--extract-js-endpoints, --archive-body, --expand-specs,
--incremental, --show-sources, --show-meta, los filtros --meta-*,
--params, --params-by-endpoint, --fuzz-placeholder, --output-dir, y
--files. El almacenamiento en caché se omite;
--format json se rechaza en favor de jsonl porque un array JSON tiene que
saber cuál es la última entrada, y --format wordlist porque no se puede saber
que un término es nuevo hasta que haya llegado cada URL.
Como el mapa de resultados por lotes nunca se rellena en este modo, una ejecución en streaming también ocupa mucha menos memoria — solo el conjunto de deduplicación de URL ya escritas.
Metadatos de captura de archivo
Un índice CDX registra más que la URL: cada captura lleva una marca de tiempo, el tipo MIME y el estado HTTP que vio el archivo, y un digest del cuerpo. urx conserva todo ello, por lo que los proveedores respaldados por CDX — wayback, cc, arquivo, y cualquier --cdx-endpoint — informan de cada URL junto con:
Campo Significado first_seenMarca de tiempo de la captura más antigua, en forma CDX de 14 dígitos (YYYYMMDDhhmmss) last_seenMarca de tiempo de la captura más reciente mimeTipo MIME de la captura más reciente que registró uno archive_statusEstado HTTP que el archivo registró en el momento de la captura digestUn digest de contenido representativo entre las capturas
archive_status no es status: status solo aparece con --check-status, que vuelve a solicitar la URL en vivo ahora, mientras que archive_status es lo que obtuvo el rastreador cuando capturó la página. Una URL puede perfectamente tener archive_status 200 y estar muerta hoy.
Cuando la misma URL proviene de varias capturas o varios archivos, los valores se fusionan: first_seen es la marca de tiempo más antigua que reportó cualquiera, last_seen la más reciente, y mime/archive_status provienen de la captura más reciente que los tenía. Los proveedores sin índice de captura (otx, vt, urlscan, zoomeye, github, bevigil, robots, sitemap, y la entrada --files) informan solo de la URL — no se inventan valores para ellos.
Cómo se muestran los metadatos depende del formato:
json / jsonl — cada campo aparece como una clave cuando tiene un valor y se omite por completo cuando no lo tiene, exactamente igual que sources.
csv — se añade una columna solo cuando al menos una fila tiene un valor para ella, por lo que una ejecución sin metadatos sigue produciendo una única columna url.
- texto plano — sin cambios por defecto, una URL desnuda por línea, para que los pipelines existentes sigan funcionando. Pasa
--show-meta para añadir los campos.```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`) informa solo URLs. Se imprime una URL en el primer avistamiento,
antes de que lleguen las capturas que ampliarían su rango `first_seen`/`last_seen`,
por lo que `--show-meta` se rechaza allí por la misma razón
que `--show-sources`.
Un acierto de caché tampoco conlleva metadatos: la caché almacena URLs, por lo que un dominio servido
desde la caché informa sus URLs sin campos de captura. Use `--no-cache` (o espere
el TTL) para una ejecución que los repoble.
### Metadatos de respuesta en vivo
`--check-status` ya envía una solicitud y espera la cabecera de respuesta, por lo que
lo que esa cabecera conlleva viene gratis: `Location`, `Content-Length` y
`Content-Type` se registran junto con el código de estado. Las redirecciones siguen sin
seguirse nunca, por lo que un estado informado siempre pertenece a la URL que se solicitó y
`location` simplemente dice hacia dónde apuntó el 3xx.
`--check-title` añade el `<title>` HTML. Es el único campo que no es gratis —
un título necesita el cuerpo de la respuesta — por lo que se sitúa detrás de su propia bandera. La lectura está
acotada dos veces (como máximo 64 KiB, y se detiene en la etiqueta de cierre) y se omite
por completo para un cuerpo que el servidor declaró como no HTML, por lo que una API JSON o una imagen
no cuestan nada. El título se colapsa en espacios en blanco, se decodifican entidades y se recorta a 200
caracteres. `--check-title` implica `--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'
La exposición sigue la regla que ya estableció la metadata del archivo: json/jsonl/csv
siempre incluyen los campos (las claves ausentes se omiten, y las columnas CSV se
añaden después de las existentes), mientras que el texto plano mantiene una URL desnuda por línea
a menos que --show-meta indique lo contrario. En la salida de texto plano el título va entre comillas, ya que
es el único valor que habitualmente contiene espacios.
Peticiones autenticadas y personalizadas
--check-status, --extract-links, --extract-js-endpoints y
--expand-specs vuelven a solicitar las URLs recopiladas desde el propio objetivo. -H
proporciona a esas peticiones las cabeceras que necesiten:```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` es repetible, acepta `Name: value`, y uno mal formado detiene la ejecución
en lugar de pasar desapercibido — un argumento que se descarta silenciosamente deja
un escaneo anónimo leyéndose como uno autenticado. `--cookie` y
`--user-agent` son atajos para las cabeceras correspondientes.
**Estas cabeceras nunca llegan a un archivo.** Solo las envían los componentes
que hablan con el objetivo: los cuatro testers anteriores, más los proveedores
`robots` y `sitemap`, que también obtienen datos del objetivo. Todos los demás proveedores
consultan web.archive.org, index.commoncrawl.org o una API de terceros, y también lo hace
`--archive-body` cuando reproduce una captura; entregarles la cookie de sesión del objetivo
enviaría una credencial a un servicio que conserva lo que recibe, sin beneficio alguno. Las consultas
a archivos mantienen el User-Agent propio de urx, que `--random-agent` sigue rotando.
### Minando Cuerpos de Respuesta Archivados
`--extract-links` obtiene cada URL recolectada del sitio en vivo, que es
exactamente el lugar equivocado para buscar las páginas que más le importan a un barrido OSINT:
las que ya no existen. `--archive-body` obtiene en su lugar los cuerpos que la Wayback
Machine almacenó. Para cada URL recolectada que lleva una marca de tiempo de captura, urx reproduce esa captura en su forma cruda
(`https://web.archive.org/web/<timestamp>id_/<url>` — el flag `id_` desactiva
la barra de herramientas de Wayback y la reescritura de enlaces, por lo que el cuerpo son los bytes originales) y
ejecuta sobre él la misma extracción de enlaces que usa `--extract-links`.```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
Por qué esto necesita muchas menos peticiones que waymore. Cada fila de CDX lleva un
digest de contenido, y dos capturas con el mismo digest son byte a byte el
mismo cuerpo. Los archivos están llenos de ellos: cada variante ?utm_source= de una página,
cada /index.html junto a su /, cada permutación de parámetros de seguimiento
sirve bytes idénticos, por lo que una lista de decenas de miles de URLs rutinariamente
se reduce a unos pocos miles de cuerpos distintos. waymore no tiene noción de esto —
descarga una respuesta por URL y lidia con el volumen mediante un tosco
límite -l 5000, que tanto golpea el archivo como trunca la cobertura. urx
reclama cada digest la primera vez que lo ve y omite cada URL posterior que
reproduciría los mismos bytes, por lo que la misma cobertura cuesta una petición por
cuerpo distinto. --archive-body-limit (por defecto 500) limita los cuerpos distintos,
no las URLs; los duplicados nunca cuentan en su contra, y --verbose informa cuántos
fueron omitidos.
Minando JavaScript archivado. La superficie de API de una aplicación moderna vive en sus bundles
como literales de cadena, y --extract-js-endpoints los obtiene del sitio en vivo — donde con frecuencia ya no están. Los bundles se nombran por hash de compilación, por lo que
app.a3f9c2.js da 404 en el momento en que el sitio se redespliega, y los endpoints que nombraba
se van con él. Ejecuta los dos flags juntos y urx mina la copia archivada
en su lugar, y los bloques <script> en línea de una página archivada junto con sus enlaces:```bash
urx example.com --archive-body --extract-js-endpoints
**Conservar los cuerpos.** Las solicitudes ya se están realizando, así que escribir los cuerpos en disco no cuesta nada extra y responde a las preguntas que ningún extractor de enlaces plantea: el comentario `<!-- staging.internal -->`, el token que una compilación de 2019 incorporó, el stack trace que nombra una versión de framework.```bash
urx example.com --archive-body --archive-body-dir ./corpus
grep -ri "api[_-]key" ./corpus
Cada archivo lleva el nombre de su URL más un hash de la misma, y corpus/index.jsonl
asigna cada archivo de vuelta a su URL, marca de tiempo de captura, digest y tipo de contenido.
Solo se almacenan cuerpos de tipo texto — HTML, script, JSON, XML, CSS, texto plano —
para que el directorio no se llene con las imágenes y fuentes del sitio. Como la
obtención se deduplica por digest, el corpus cubre mucho más del objetivo por
petición de lo que lo haría una respuesta por URL.
Detalles que conviene conocer:
- Solo cualifican las URLs con marca de tiempo de captura. Los proveedores CDX (
wayback,
cc, arquivo) la proporcionan; la entrada --files, los proveedores no CDX y los resultados
en caché (la caché almacena solo URLs) no tienen ninguna. urx lo indica cuando no hay
nada que reproducir — pasa --no-cache para obtener capturas frescas.
- Se reproduce la captura más reciente de cada URL. Una marca de tiempo reportada por otro
archivo recae en la captura de Wayback más cercana; una URL que la Wayback Machine nunca
vio responde 404 y se omite. Las capturas que el archivo registró como errores no
se minan, exactamente igual que
--extract-links ignora las páginas de error en vivo.
- Los enlaces descubiertos pasan por los mismos filtros, validación de host y transformaciones
de salida que todo lo demás, y cada cuerpo está limitado a 10 MiB.
--rate-limit, --rate-limit-by wayback=N, --parallel, --proxy,
--timeout y --retries se aplican a las peticiones de reproducción.
- Incompatible con
--stream, como toda opción que se ejecuta después de la recolección.
Expansión de especificaciones de API
Un barrido -p only-api encuentra /swagger.json, /openapi.yaml y /v3/api-docs
y luego nunca los abre: --extract-links analiza HTML, --extract-js-endpoints
descarta los cuerpos application/json, y --archive-body ejecuta el analizador HTML sobre
lo que devuelva el archivo. --expand-specs los lee y expande cada ruta
que describen en el conjunto de resultados — una petición compra toda la
superficie documentada, exacta y ya parametrizada.```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
Lo que se expande:
* **OpenAPI 3.x** — `servers[].url` (absoluta, relativa al documento y con plantillas,
con `{var}` resuelto desde `variables[var].default` o el primer valor de `enum`)
cruzado con cada clave de `paths`; los `servers` propios de un elemento de ruta
anulan los del documento.
* **Swagger 2.0** — `schemes` × `host` + `basePath`, cada parte recurriendo a
la parte correspondiente de la URL del propio documento. Se descartan `ws`/`wss`.
* **Introspección de GraphQL** — una URL por cada campo de consulta, mutación y suscripción,
escrita como el endpoint más `?query=…`. Un esquema guardado como archivo
se resuelve a su endpoint (`/graphql/schema.json` → `/graphql`).
Se leen tanto JSON como YAML. Los objetivos se eligen primero por nombre y sin coste (una
subcadena marcadora de especificación — `swagger`, `openapi`, `api-docs`, `graphql`,
`introspection` — más una extensión `json`/`yaml`/`yml` cuando la hay, de modo que
`swagger-ui.html` no cuesta ninguna petición), y luego por el `Content-Type` de la respuesta. Las plantillas de ruta
se emiten tal como las escribe el documento (`/users/{id}`, no
`/users/%7Bid%7D`). Los cuerpos están limitados a 10 MiB, y un documento YAML con más
de 32 referencias de alias se rechaza antes de parsearlo para descartar bombas de expansión.
`--max-spec-files` (por defecto 50) limita los documentos obtenidos. Con
`--archive-body` también activado, una especificación archivada se lee como una sin coste adicional
de peticiones — el cuerpo ya se estaba obteniendo.
### robots.txt y sitemap.xml archivados
Los proveedores `robots` y `sitemap` leen los archivos *en vivo*, que solo dicen lo que
un sitio oculta o lista hoy. `--archived-discovery` también lee cada versión distinta
de esos archivos que la Wayback Machine ha almacenado. Un `Disallow:` de 2015
nombra rutas que el sitio ha dejado de mencionar desde entonces — a menudo porque estaban
destinadas a ser olvidadas, no porque hayan desaparecido — y un sitemap antiguo lista
todo lo que el sitio alguna vez quiso que se rastreara.```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
Cómo funciona y por qué es económico:
- Las versiones de un documento se listan con una consulta CDX por archivo
(
robots.txt, sitemap.xml, sitemap_index.xml, sitemap.txt), usando
collapse=digest para que las capturas consecutivas que sirvieron los mismos bytes se plieguen
en una sola fila. Solo se solicitan las filas registradas como exitosas: el índice pliega
www. y el dominio raíz en un solo listado, y sus filas intercaladas 301/200
de otro modo anulan el collapse — para github.com/robots.txt eso son 325k filas
sin el filtro y 14k con él, para las mismas 107 versiones distintas.
- Cada versión distinta se reproduce en forma cruda (
/web/<timestamp>id_/…) y
se entrega al mismo parser que el archivo en vivo. Sin un segundo parser: un robots.txt de 2015
se lee con exactamente las mismas reglas que el actual, incluidos
las protecciones de ruta absoluta y omisión de patrones. Un <sitemapindex> archivado se
sigue hacia sus hijos tal como estaban en ese mismo momento.
- Las capturas que el archivo registró como errores (el robots.txt de github.com fue un 401
durante parte de 2007) se omiten sin una solicitud y se reportan bajo
--verbose únicamente.
--archived-discovery-limit (por defecto 50) limita los documentos obtenidos por
dominio por cada proveedor archivado, las versiones más recientes primero; los sitemaps anidados
cuentan. --verbose indica cuándo el límite truncó la lista.
- Las variantes archivadas se ejecutan como sus propias instancias de proveedor — "Robots.txt
(archived)" y "Sitemap (archived)" en
--stats y --show-sources — pero
bajo los ids existentes robots / sitemap, por lo que --exclude-robots,
--exclude-sitemap y --rate-limit-by robots=N gobiernan tanto las lecturas en vivo como las
archivadas. --from / --to acotan qué versiones se consideran.
- Funciona con
--stream; es un proveedor como cualquier otro.
Filtrado del lado del archivo
--archive-status, --archive-mime, --from y --to son evaluados por el
índice CDX del archivo en lugar de por urx. Vale la pena conocer dos consecuencias:
- Se aplican solo a los proveedores respaldados por CDX —
wayback, cc, arquivo y
cualquier --cdx-endpoint. Otros proveedores los ignoran; urx advierte cuando ninguno está
habilitado.
- Los archivos no comparten un único dialecto de filtros. Wayback Machine (y cualquier
endpoint
--cdx-dialect classic) trata los valores como expresiones regulares, por lo que
--archive-status "30." coincide con cualquier 3xx. Common Crawl, Arquivo.pt y los endpoints
pywb coinciden exactamente, y su índice combina filtros repetidos con AND —
por lo que una lista positiva multivalor como --archive-status 200,301 es
insatisfacible allí. urx omite ese filtro para esos proveedores (con una
advertencia) en lugar de enviar una consulta que volvería vacía. Las exclusiones
multivalor significan "ni esto ni aquello" y funcionan en todas partes.
Use --archive-status cuando quiera lo que el archivo registró en el momento del rastreo y
--check-status / --include-status cuando quiera el estado del objetivo ahora;
este último vuelve a solicitar cada URL.
Endpoints CDX personalizados
Todo archivo web construido sobre pywb, OutbackCDX o el servidor CDX de Internet Archive
expone la misma API de consulta. En lugar de codificar un proveedor por
archivo, --cdx-endpoint URL convierte cualquier servidor de ese tipo en un proveedor al
instante:```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
* El id del proveedor es `cdx:<host>` (`cdx:vefsafn.is`), que es lo que usan
`--exclude-providers`, `--rate-limit-by`, `--stats` y `--show-sources`.
Nombrar un endpoint lo habilita; no se necesita ninguna entrada en `--providers`, y
`--providers cdx:vefsafn.is` lo ejecuta en solitario. `--list-providers` muestra los
endpoints nombrados en la misma línea de comandos con los ids con los que se ejecutarán.
* Todo lo que respetan los proveedores CDX integrados también se aplica aquí: `--subs`,
`--from`/`--to`, los filtros `--archive-*`, la paginación, `--rate-limit` y
los metadatos de captura descritos anteriormente.
* `--cdx-dialect classic|pywb` nombra el dialecto del servidor (los nombres de campo, la
semántica de filtrado, el formato de fila y el esquema de paginación se derivan de él — véase
"Filtrado del lado del archivo"). Si no se establece, urx sondea el endpoint una vez por ejecución
y recurre a `pywb`, el dialecto más común; establécelo explícitamente cuando el
sondeo no pueda determinarlo (una respuesta vacía para un dominio desconocido, por ejemplo).
* También se puede establecer en el archivo de configuración (`cdx_endpoint = [...]`, `cdx_dialect`).
**Endpoints verificados.** A fecha de esta escritura, el único endpoint público confirmado
que funciona de principio a fin es `https://vefsafn.is/cdx` (el archivo web islandés de Landsbókasafn, dialecto pywb). Dos cosas que hay que saber sobre él: ignora `limit`, `page`
y `showNumPages` y devuelve el conjunto completo de resultados para cada consulta, lo cual
urx maneja; y tras unas cuantas peticiones puede empezar a responder con una
página de protección contra bots al estilo Anubis ("Session Verification"). urx detecta una respuesta HTML en lugar de filas CDX y lo reporta como un error de proveedor nombrando el
endpoint — nunca se cuenta como "sin URLs". Si te encuentras con esto, reduce la velocidad con
`--rate-limit-by cdx:vefsafn.is=1` o reinténtalo más tarde.
**Se sabe que no funcionan.** El UK Web Archive (`webarchive.org.uk`), el archivo web de la Library of
Congress (`webarchive.loc.gov`), la Bibliotheca Alexandrina y la
National Library of Australia (`web.archive.org.au`) están todos detrás de protección contra bots o redirecciones que bloquean sus API CDX desde un cliente de línea de comandos.
urx no intenta sortear eso, por lo que apuntar `--cdx-endpoint` a ellos
produce el error de respuesta HTML mencionado anteriormente.
### Almacenamiento en caché y escaneo incremental
Urx admite almacenamiento en caché para mejorar el rendimiento en escaneos repetidos y escaneo incremental para descubrir solo nuevas URLs.```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
Gestión de la caché
urx cache inspecciona y mantiene la caché sin tocar la base de datos
manualmente. Cada subcomando respeta los mismos --cache-type, --cache-path,
--redis-url y --cache-ttl que un escaneo, y los cinco funcionan con ambos
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'
La coincidencia de dominios no distingue entre mayúsculas y minúsculas y es **exacta** a menos que el patrón contenga
`*` — un valor predeterminado de subcadena habría permitido que `drop example.com` eliminara también
`notexample.com`. `clear` pregunta antes de eliminar y rechaza una
entrada estándar no interactiva en lugar de asumir una respuesta, `drop` nombra cualquier patrón
que no coincidió con nada, consultar la caché nunca crea una, y Redis se recorre
con `SCAN` en lugar del bloqueante `KEYS` (con cualquier contraseña en `--redis-url`
redactada antes de imprimirse).
#### Casos de uso de caché```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
Notificaciones por Webhook
--notify <URL> envía por POST un resumen de la ejecución a un webhook cuando esta finaliza,
lo que convierte --incremental en un monitor: colócalo en cron y el webhook
se activará solo cuando aparezca algo nuevo.```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` es `new` por defecto: no se envía nada cuando la ejecución emite cero
URLs, por lo que una ejecución silenciosa de cron permanece silenciosa. `always` envía sin importar qué; `never`
mantiene la configuración pero deshabilita el envío.
- `--notify-format json` (por defecto) envía el esquema de urx: `domains`,
`incremental`, `url_count`, `new_url_count`, `elapsed_ms`, una lista `providers` por proveedor
(los mismos números que imprime `--stats`), y una `sample` de hasta
20 URLs emitidas con `sample_truncated` establecido cuando se encontraron más.
`slack` envía `{"text": ...}` y `discord` envía `{"content": ...}` con un
mensaje breve legible por humanos; los mensajes más largos de lo que permite el servicio se
cortan en un límite de línea y terminan con `[truncated: N lines cut ...]`.
- La entrega nunca cambia el código de salida. Las URLs ya están en stdout o en
`--output` en el momento en que se llama al webhook, por lo que un webhook muerto es una advertencia
en stderr y la ejecución aún sale con 0. `--verbose` muestra el estado de la respuesta.
- La URL del webhook es una credencial. urx nunca imprime más que su esquema y
host — ni en `--verbose`, ni en advertencias, ni en `--stats`. Para mantenerla fuera
de una configuración que se versiona, ponla en `URX_NOTIFY_URL` o como
`notify_url` en el archivo de configuración del proveedor; `[notify].url` en la configuración principal
también funciona. La precedencia es CLI/env > configuración del proveedor > configuración principal.
- La solicitud respeta `--proxy`, `--proxy-auth`, `--timeout` y `--insecure`.
`--network-scope` no aplica: particiona el tráfico dirigido al objetivo
y a los archivos, y el webhook es tu propio endpoint.
- `--silent` aún envía (ese es el caso de uso principal); solo oculta los
diagnósticos.
## Integración con Otras Herramientas
Urx funciona bien en pipelines con otras herramientas de seguridad y reconocimiento:```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
Inspiración
Urx se inspiró en gau (GetAllUrls), una herramienta que obtiene URLs conocidas de AlienVault's Open Threat Exchange, la Wayback Machine y Common Crawl. Aunque comparte una funcionalidad central similar, Urx fue construido desde cero en Rust con un enfoque en el rendimiento, la concurrencia y capacidades de filtrado ampliadas.
Contribuir
Urx es un proyecto de código abierto y hecho con ❤️
si quieres contribuir a este proyecto, consulta CONTRIBUTING.md y envía un Pull-Request con tus geniales aportes.