Z-Sans controls scanning behavior through a YAML configuration file. The default path is breeding-config.yaml.
Configuration Loading Mechanism¶
Generating the Default Configuration¶
If the target file does not exist, --init performs a full-file copy of templates/breeding-config.yaml (with complete Chinese comments); it falls back to dumping the built-in defaults only when the template is unavailable.
Configuration Merging Rules¶
The configuration file is merged with the built-in defaults using a recursive deep merge:
- Dictionaries are merged recursively (write only the fields you need; the rest keep their defaults)
- Lists and scalars are overwritten directly
- Therefore you can write only the fields you need, e.g.
asset_types.domain.priority
List Overwrite Pitfall
List fields such as plugins.disabled are fully replaced, not appended. If you write plugins.disabled: [a] in your config but the built-in default also contains b, then b will be lost during the merge. Merge order: built-in defaults → user config; the user config completely overwrites identically-named lists.
Configuration Structure Overview¶
strategy: priority_based # Breeding strategy
asset_scope: {...} # Scan scope
concurrency: {...} # Concurrency
max_depth: 4 # Maximum depth
resource_limits: {...} # Asset limits
asset_types: {...} # Per-type toggles and tools
output: {...} # Output
external_tools: {...} # External tools
language: {...} # Language
exclusions: {...} # Exclusion rules
http: {...} # HTTP behavior
plugins: {...} # Plugins (template only)
checkpoint: {...} # Checkpoint (template only)
monitoring: {...} # Monitoring (template only)
Global¶
strategy: priority_based # priority_based | depth_first | breadth_first | time_based
max_depth: 4 # Maximum discovery depth (can be overridden by --depth)
asset_scope — Asset Scan Scope¶
asset_scope:
restrict_to_seed_domains: true # Restrict to seed domains only
restrict_to_seed_ip_ranges: false # Restrict to seed IP ranges only
include_subdomains: true # Include subdomains
include_ip_ranges: true # Include IP ranges
seed_scope: registrable # Seed scope expansion, see below
seed_scope — Seed Scope Expansion¶
Controls whether/how a seed domain is expanded for relevance matching (restrict_to_seed_domains: true):
| Value | Behavior | Example |
|---|---|---|
registrable (default) |
Expand to the registrable domain (eTLD+1) computed from the bundled Public Suffix List | Seed www.example.com → also covers example.com and all its subdomains. Seed example.co.uk → stays example.co.uk, because co.uk is a public suffix and will not be matched against unrelated *.co.uk sites |
exact |
No expansion at all — only the seed domain itself and its subdomains | Seed www.example.com → covers *.www.example.com only; mail.example.com is out of scope |
The Public Suffix List data is bundled at core/data/public_suffix_list.dat (ICANN + Private sections), so this works offline with no extra dependency; if that file is missing, Z-Sans falls back to an installed publicsuffixlist/publicsuffix2 package and finally to a built-in table of common multi-part suffixes.
Why not just take the last two labels?
Older versions expanded seeds by taking the last two labels (parts[-2:]). That works for www.example.com → example.com, but turns example.co.uk into the public suffix co.uk — making every third-party *.co.uk site look "seed-related" and dragging the scan off-scope. The PSL-based mode fixes both directions; use exact when you want zero expansion.
Seeds are also normalized (lowercase, trailing dot stripped) before matching.
concurrency — Concurrency¶
concurrency:
max_tasks: 20 # Global maximum concurrent tasks (thread pool size)
tools: # Per-tool independent concurrency
subfinder: 2
naabu: 2
jsfinder: 2
resource_limits — Asset Quantity Limits¶
Assets exceeding the limits are marked excluded:
asset_types — Asset Types and Tool Switches¶
Each type can be configured with: enabled state, depth limit, priority, and tool switches.
asset_types:
domain:
enabled: true
depth_limit: 3 # Maximum mining depth for this type
priority: 10 # Higher value = processed first
tools:
subfinder: true # Subdomain discovery
free_subfinder: false # Free passive-source aggregation
crtsh: true # CRT.sh certificate transparency logs
dns_brute: true # Built-in DNS brute force enumeration
dnsx: true # DNS resolution verification
ip:
enabled: true
depth_limit: 4
priority: 8
tools:
naabu: true # Port scanning
reverse_dns: true # Reverse DNS
port_range: '1-1024,3306,3389,5432,5900,6379,7001,8000-8500,8888,9000-9100,9200,27017,11211' # (template only, not in this file)
url:
enabled: true
depth_limit: 5
priority: 10
tools:
jsfinder: true # JS extraction
link_extract: true # Hyperlink extraction
fingerprint: true # Site fingerprinting
title_extraction: # Title extraction
enabled: true
max_length: 50 # Maximum length; longer titles are truncated
show_in_report: true
show_in_csv: true
port:
enabled: true
depth_limit: 5
priority: 7
tools:
service_identify: true # Service identification
js:
enabled: true
depth_limit: 4
priority: 6
tools:
jsfinder: true
Default Priorities
If priority is not configured, the built-in defaults are domain=5, url=4, ip=3, port=2, js=1.
output — Output¶
output:
dir: output # Output root directory (can be overridden by -o)
graph_format: json # Reserved field, actually controlled by formats
asset_report: json # Reserved field
keep_eliminated_assets: true # Keep eliminated assets in the report
formats:
json: true # JSON asset graph
csv: true # Two CSVs (assets + relations)
graphml: true # GraphML topology
html: true # HTML report
auto_open: true # Automatically open the report when the scan finishes
output_prefix: zsans # Output file prefix (template only)
external_tools — External Tools¶
external_tools:
paths: # Custom paths; if empty, tools are located via the probe order
subfinder: null # e.g. /usr/bin/subfinder
naabu: null # e.g. D:\naabu\naabu.exe
ehole: null # EHole fingerprint tool
whatweb: null # WhatWeb fingerprint tool
fingerprint: # Fingerprint recognition
enabled: true # Use the local multi-architecture EHole
engine: auto # auto | ehole | whatweb | both | none
jsfinder_timeout: 30 # JSfinder timeout (seconds, template only)
Tool probe order (see Tool Integration):
external_tools.paths.<name>from the configuration- Built-in directories
tools/<name>/.../assets/<name> - System PATH
language — Internationalization¶
language:
default_language: en # zh_CN or en
supported_languages:
- zh_CN
- en
locale_dir: i18n # Language files directory
See Internationalization for details.
exclusions — Exclusion Rules¶
Assets matching exclusion rules are marked excluded and trigger on_asset_excluded.
exclusions:
domains: # Excluded domains (exact match or its suffix .<domain>)
- example.com
- test.local
ips: # Excluded IPs (exact match)
- 127.0.0.1
- 0.0.0.0
urls: # URLs containing a keyword are excluded
- login
- logout
ports: [] # Excluded ports
patterns: # Regex exclusions (common static asset suffixes)
- \.(css|jpg|jpeg|png|gif|svg|woff|woff2|ttf|eot)$
http — HTTP Behavior¶
http:
timeout: 10 # Per-request timeout (seconds)
retries: 3 # Number of retries (retries on 429/5xx)
user_agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36
verify_ssl: false # Verify SSL certificates (set to false for self-signed sites)
proxy: null # HTTP proxy
follow_redirects: true # Follow redirects
redirect_as_new_asset: true # Record the redirect target as a new asset
max_redirects: 5 # Maximum redirects per link
plugins — Plugins (provided by the template)¶
plugins:
dir: null # Plugin directory; when null, uses plugins/ at the project root
disabled: [] # List of plugin names to disable
checkpoint — Resumable Scanning (provided by the template)¶
checkpoint:
enabled: true # Enable checkpoint saving
interval: 50 # Save once every 50 processed assets
file: null # Checkpoint file path; when null, uses <output_dir>/checkpoint.json
monitoring — Monitoring (provided by the template)¶
monitoring:
enabled: false # Whether it is enabled (actual effect is determined by --watch)
interval: 3600 # Polling interval (seconds)
webhook_url: null # Change-alert Webhook (POST JSON)
monitoring.enabled is not actually consulted
Entering monitoring mode is decided by the CLI flag --watch; the code only reads interval and webhook_url.
Top-Level Priority Chain¶
| Configuration Source | Priority |
|---|---|
CLI flags (--depth, -o) |
Highest (overrides configuration) |
breeding-config.yaml user configuration |
Medium |
Built-in DEFAULT_CONFIG |
Lowest (fallback) |