Showing preview only (4,990K chars total). Download the full file or copy to clipboard to get everything.
Repository: blacklanternsecurity/bbot
Branch: stable
Commit: ec1acae429e4
Files: 543
Total size: 4.7 MB
Directory structure:
gitextract_abbww2k9/
├── .gitattributes
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ ├── dependabot.yml
│ └── workflows/
│ ├── benchmark.yml
│ ├── codeql.yml
│ ├── distro_tests.yml
│ ├── docs_updater.yml
│ ├── tests.yml
│ └── version_updater.yml
├── .gitignore
├── .gitmodules
├── .pre-commit-config.yaml
├── Dockerfile
├── Dockerfile.full
├── LICENSE
├── README.md
├── bbot/
│ ├── __init__.py
│ ├── cli.py
│ ├── core/
│ │ ├── __init__.py
│ │ ├── config/
│ │ │ ├── __init__.py
│ │ │ ├── files.py
│ │ │ └── logger.py
│ │ ├── core.py
│ │ ├── engine.py
│ │ ├── event/
│ │ │ ├── __init__.py
│ │ │ ├── base.py
│ │ │ └── helpers.py
│ │ ├── flags.py
│ │ ├── helpers/
│ │ │ ├── __init__.py
│ │ │ ├── async_helpers.py
│ │ │ ├── bloom.py
│ │ │ ├── cache.py
│ │ │ ├── command.py
│ │ │ ├── depsinstaller/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── installer.py
│ │ │ │ └── sudo_askpass.py
│ │ │ ├── diff.py
│ │ │ ├── dns/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── brute.py
│ │ │ │ ├── dns.py
│ │ │ │ ├── engine.py
│ │ │ │ ├── helpers.py
│ │ │ │ └── mock.py
│ │ │ ├── files.py
│ │ │ ├── git.py
│ │ │ ├── helper.py
│ │ │ ├── interactsh.py
│ │ │ ├── libmagic.py
│ │ │ ├── misc.py
│ │ │ ├── names_generator.py
│ │ │ ├── ntlm.py
│ │ │ ├── process.py
│ │ │ ├── ratelimiter.py
│ │ │ ├── regex.py
│ │ │ ├── regexes.py
│ │ │ ├── url.py
│ │ │ ├── validators.py
│ │ │ ├── web/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── client.py
│ │ │ │ ├── engine.py
│ │ │ │ ├── envelopes.py
│ │ │ │ ├── ssl_context.py
│ │ │ │ └── web.py
│ │ │ ├── wordcloud.py
│ │ │ └── yara_helper.py
│ │ ├── modules.py
│ │ ├── multiprocess.py
│ │ └── shared_deps.py
│ ├── db/
│ │ └── sql/
│ │ └── models.py
│ ├── defaults.yml
│ ├── errors.py
│ ├── logger.py
│ ├── modules/
│ │ ├── __init__.py
│ │ ├── ajaxpro.py
│ │ ├── anubisdb.py
│ │ ├── apkpure.py
│ │ ├── aspnet_bin_exposure.py
│ │ ├── azure_realm.py
│ │ ├── azure_tenant.py
│ │ ├── baddns.py
│ │ ├── baddns_direct.py
│ │ ├── baddns_zone.py
│ │ ├── badsecrets.py
│ │ ├── base.py
│ │ ├── bevigil.py
│ │ ├── bucket_amazon.py
│ │ ├── bucket_digitalocean.py
│ │ ├── bucket_file_enum.py
│ │ ├── bucket_firebase.py
│ │ ├── bucket_google.py
│ │ ├── bucket_microsoft.py
│ │ ├── bufferoverrun.py
│ │ ├── builtwith.py
│ │ ├── bypass403.py
│ │ ├── c99.py
│ │ ├── censys_dns.py
│ │ ├── censys_ip.py
│ │ ├── certspotter.py
│ │ ├── chaos.py
│ │ ├── code_repository.py
│ │ ├── credshed.py
│ │ ├── crt.py
│ │ ├── crt_db.py
│ │ ├── deadly/
│ │ │ └── legba.py
│ │ ├── dehashed.py
│ │ ├── digitorus.py
│ │ ├── dnsbimi.py
│ │ ├── dnsbrute.py
│ │ ├── dnsbrute_mutations.py
│ │ ├── dnscaa.py
│ │ ├── dnscommonsrv.py
│ │ ├── dnsdumpster.py
│ │ ├── dnstlsrpt.py
│ │ ├── docker_pull.py
│ │ ├── dockerhub.py
│ │ ├── dotnetnuke.py
│ │ ├── emailformat.py
│ │ ├── extractous.py
│ │ ├── ffuf.py
│ │ ├── ffuf_shortnames.py
│ │ ├── filedownload.py
│ │ ├── fingerprintx.py
│ │ ├── fullhunt.py
│ │ ├── generic_ssrf.py
│ │ ├── git.py
│ │ ├── git_clone.py
│ │ ├── gitdumper.py
│ │ ├── github_codesearch.py
│ │ ├── github_org.py
│ │ ├── github_usersearch.py
│ │ ├── github_workflows.py
│ │ ├── gitlab_com.py
│ │ ├── gitlab_onprem.py
│ │ ├── google_playstore.py
│ │ ├── gowitness.py
│ │ ├── graphql_introspection.py
│ │ ├── hackertarget.py
│ │ ├── host_header.py
│ │ ├── httpx.py
│ │ ├── hunt.py
│ │ ├── hunterio.py
│ │ ├── iis_shortnames.py
│ │ ├── internal/
│ │ │ ├── __init__.py
│ │ │ ├── aggregate.py
│ │ │ ├── base.py
│ │ │ ├── cloudcheck.py
│ │ │ ├── dnsresolve.py
│ │ │ ├── excavate.py
│ │ │ ├── speculate.py
│ │ │ └── unarchive.py
│ │ ├── ip2location.py
│ │ ├── ipneighbor.py
│ │ ├── ipstack.py
│ │ ├── jadx.py
│ │ ├── leakix.py
│ │ ├── lightfuzz/
│ │ │ ├── lightfuzz.py
│ │ │ └── submodules/
│ │ │ ├── __init__.py
│ │ │ ├── base.py
│ │ │ ├── cmdi.py
│ │ │ ├── crypto.py
│ │ │ ├── esi.py
│ │ │ ├── path.py
│ │ │ ├── serial.py
│ │ │ ├── sqli.py
│ │ │ ├── ssti.py
│ │ │ └── xss.py
│ │ ├── medusa.py
│ │ ├── myssl.py
│ │ ├── newsletters.py
│ │ ├── ntlm.py
│ │ ├── nuclei.py
│ │ ├── oauth.py
│ │ ├── otx.py
│ │ ├── output/
│ │ │ ├── __init__.py
│ │ │ ├── asset_inventory.py
│ │ │ ├── base.py
│ │ │ ├── csv.py
│ │ │ ├── discord.py
│ │ │ ├── emails.py
│ │ │ ├── http.py
│ │ │ ├── json.py
│ │ │ ├── mysql.py
│ │ │ ├── neo4j.py
│ │ │ ├── nmap_xml.py
│ │ │ ├── postgres.py
│ │ │ ├── python.py
│ │ │ ├── slack.py
│ │ │ ├── splunk.py
│ │ │ ├── sqlite.py
│ │ │ ├── stdout.py
│ │ │ ├── subdomains.py
│ │ │ ├── teams.py
│ │ │ ├── txt.py
│ │ │ ├── web_parameters.py
│ │ │ ├── web_report.py
│ │ │ └── websocket.py
│ │ ├── paramminer_cookies.py
│ │ ├── paramminer_getparams.py
│ │ ├── paramminer_headers.py
│ │ ├── passivetotal.py
│ │ ├── pgp.py
│ │ ├── portfilter.py
│ │ ├── portscan.py
│ │ ├── postman.py
│ │ ├── postman_download.py
│ │ ├── rapiddns.py
│ │ ├── reflected_parameters.py
│ │ ├── report/
│ │ │ ├── affiliates.py
│ │ │ ├── asn.py
│ │ │ └── base.py
│ │ ├── retirejs.py
│ │ ├── robots.py
│ │ ├── securitytrails.py
│ │ ├── securitytxt.py
│ │ ├── shodan_dns.py
│ │ ├── shodan_idb.py
│ │ ├── sitedossier.py
│ │ ├── skymem.py
│ │ ├── smuggler.py
│ │ ├── social.py
│ │ ├── sslcert.py
│ │ ├── subdomaincenter.py
│ │ ├── subdomainradar.py
│ │ ├── telerik.py
│ │ ├── templates/
│ │ │ ├── bucket.py
│ │ │ ├── censys.py
│ │ │ ├── github.py
│ │ │ ├── gitlab.py
│ │ │ ├── postman.py
│ │ │ ├── shodan.py
│ │ │ ├── sql.py
│ │ │ ├── subdomain_enum.py
│ │ │ └── webhook.py
│ │ ├── trickest.py
│ │ ├── trufflehog.py
│ │ ├── url_manipulation.py
│ │ ├── urlscan.py
│ │ ├── vhost.py
│ │ ├── viewdns.py
│ │ ├── virustotal.py
│ │ ├── wafw00f.py
│ │ ├── wayback.py
│ │ └── wpscan.py
│ ├── presets/
│ │ ├── baddns-intense.yml
│ │ ├── cloud-enum.yml
│ │ ├── code-enum.yml
│ │ ├── email-enum.yml
│ │ ├── fast.yml
│ │ ├── kitchen-sink.yml
│ │ ├── nuclei/
│ │ │ ├── nuclei-budget.yml
│ │ │ ├── nuclei-intense.yml
│ │ │ ├── nuclei-technology.yml
│ │ │ └── nuclei.yml
│ │ ├── spider-intense.yml
│ │ ├── spider.yml
│ │ ├── subdomain-enum.yml
│ │ ├── tech-detect.yml
│ │ ├── web/
│ │ │ ├── dirbust-heavy.yml
│ │ │ ├── dirbust-light.yml
│ │ │ ├── dotnet-audit.yml
│ │ │ ├── iis-shortnames.yml
│ │ │ ├── lightfuzz-heavy.yml
│ │ │ ├── lightfuzz-light.yml
│ │ │ ├── lightfuzz-medium.yml
│ │ │ ├── lightfuzz-superheavy.yml
│ │ │ ├── lightfuzz-xss.yml
│ │ │ └── paramminer.yml
│ │ ├── web-basic.yml
│ │ ├── web-screenshots.yml
│ │ └── web-thorough.yml
│ ├── scanner/
│ │ ├── __init__.py
│ │ ├── dispatcher.py
│ │ ├── manager.py
│ │ ├── preset/
│ │ │ ├── __init__.py
│ │ │ ├── args.py
│ │ │ ├── conditions.py
│ │ │ ├── environ.py
│ │ │ ├── path.py
│ │ │ └── preset.py
│ │ ├── scanner.py
│ │ ├── stats.py
│ │ └── target.py
│ ├── scripts/
│ │ ├── benchmark_report.py
│ │ └── docs.py
│ ├── test/
│ │ ├── __init__.py
│ │ ├── bbot_fixtures.py
│ │ ├── benchmarks/
│ │ │ ├── __init__.py
│ │ │ ├── test_bloom_filter_benchmarks.py
│ │ │ ├── test_closest_match_benchmarks.py
│ │ │ ├── test_event_validation_benchmarks.py
│ │ │ ├── test_excavate_benchmarks.py
│ │ │ ├── test_ipaddress_benchmarks.py
│ │ │ └── test_weighted_shuffle_benchmarks.py
│ │ ├── conftest.py
│ │ ├── coverage.cfg
│ │ ├── fastapi_test.py
│ │ ├── owasp_mastg.apk
│ │ ├── run_tests.sh
│ │ ├── test.conf
│ │ ├── test_output.ndjson
│ │ ├── test_step_1/
│ │ │ ├── __init__.py
│ │ │ ├── test__module__tests.py
│ │ │ ├── test_bbot_fastapi.py
│ │ │ ├── test_bloom_filter.py
│ │ │ ├── test_cli.py
│ │ │ ├── test_command.py
│ │ │ ├── test_config.py
│ │ │ ├── test_depsinstaller.py
│ │ │ ├── test_dns.py
│ │ │ ├── test_docs.py
│ │ │ ├── test_engine.py
│ │ │ ├── test_event_seeds.py
│ │ │ ├── test_events.py
│ │ │ ├── test_files.py
│ │ │ ├── test_helpers.py
│ │ │ ├── test_manager_deduplication.py
│ │ │ ├── test_manager_scope_accuracy.py
│ │ │ ├── test_modules_basic.py
│ │ │ ├── test_presets.py
│ │ │ ├── test_python_api.py
│ │ │ ├── test_regexes.py
│ │ │ ├── test_scan.py
│ │ │ ├── test_scope.py
│ │ │ ├── test_target.py
│ │ │ ├── test_web.py
│ │ │ └── test_web_envelopes.py
│ │ ├── test_step_2/
│ │ │ ├── __init__.py
│ │ │ ├── module_tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── base.py
│ │ │ │ ├── test_module_affiliates.py
│ │ │ │ ├── test_module_aggregate.py
│ │ │ │ ├── test_module_ajaxpro.py
│ │ │ │ ├── test_module_anubisdb.py
│ │ │ │ ├── test_module_apkpure.py
│ │ │ │ ├── test_module_asn.py
│ │ │ │ ├── test_module_aspnet_bin_exposure.py
│ │ │ │ ├── test_module_asset_inventory.py
│ │ │ │ ├── test_module_azure_realm.py
│ │ │ │ ├── test_module_azure_tenant.py
│ │ │ │ ├── test_module_baddns.py
│ │ │ │ ├── test_module_baddns_direct.py
│ │ │ │ ├── test_module_baddns_zone.py
│ │ │ │ ├── test_module_badsecrets.py
│ │ │ │ ├── test_module_bevigil.py
│ │ │ │ ├── test_module_bucket_amazon.py
│ │ │ │ ├── test_module_bucket_digitalocean.py
│ │ │ │ ├── test_module_bucket_file_enum.py
│ │ │ │ ├── test_module_bucket_firebase.py
│ │ │ │ ├── test_module_bucket_google.py
│ │ │ │ ├── test_module_bucket_microsoft.py
│ │ │ │ ├── test_module_bufferoverrun.py
│ │ │ │ ├── test_module_builtwith.py
│ │ │ │ ├── test_module_bypass403.py
│ │ │ │ ├── test_module_c99.py
│ │ │ │ ├── test_module_censys_dns.py
│ │ │ │ ├── test_module_censys_ip.py
│ │ │ │ ├── test_module_certspotter.py
│ │ │ │ ├── test_module_chaos.py
│ │ │ │ ├── test_module_cloudcheck.py
│ │ │ │ ├── test_module_code_repository.py
│ │ │ │ ├── test_module_credshed.py
│ │ │ │ ├── test_module_crt.py
│ │ │ │ ├── test_module_crt_db.py
│ │ │ │ ├── test_module_csv.py
│ │ │ │ ├── test_module_dehashed.py
│ │ │ │ ├── test_module_digitorus.py
│ │ │ │ ├── test_module_discord.py
│ │ │ │ ├── test_module_dnsbimi.py
│ │ │ │ ├── test_module_dnsbrute.py
│ │ │ │ ├── test_module_dnsbrute_mutations.py
│ │ │ │ ├── test_module_dnscaa.py
│ │ │ │ ├── test_module_dnscommonsrv.py
│ │ │ │ ├── test_module_dnsdumpster.py
│ │ │ │ ├── test_module_dnsresolve.py
│ │ │ │ ├── test_module_dnstlsrpt.py
│ │ │ │ ├── test_module_docker_pull.py
│ │ │ │ ├── test_module_dockerhub.py
│ │ │ │ ├── test_module_dotnetnuke.py
│ │ │ │ ├── test_module_emailformat.py
│ │ │ │ ├── test_module_emails.py
│ │ │ │ ├── test_module_excavate.py
│ │ │ │ ├── test_module_extractous.py
│ │ │ │ ├── test_module_ffuf.py
│ │ │ │ ├── test_module_ffuf_shortnames.py
│ │ │ │ ├── test_module_filedownload.py
│ │ │ │ ├── test_module_fingerprintx.py
│ │ │ │ ├── test_module_fullhunt.py
│ │ │ │ ├── test_module_generic_ssrf.py
│ │ │ │ ├── test_module_git.py
│ │ │ │ ├── test_module_git_clone.py
│ │ │ │ ├── test_module_gitdumper.py
│ │ │ │ ├── test_module_github_codesearch.py
│ │ │ │ ├── test_module_github_org.py
│ │ │ │ ├── test_module_github_usersearch.py
│ │ │ │ ├── test_module_github_workflows.py
│ │ │ │ ├── test_module_gitlab_com.py
│ │ │ │ ├── test_module_gitlab_onprem.py
│ │ │ │ ├── test_module_google_playstore.py
│ │ │ │ ├── test_module_gowitness.py
│ │ │ │ ├── test_module_graphql_introspection.py
│ │ │ │ ├── test_module_hackertarget.py
│ │ │ │ ├── test_module_host_header.py
│ │ │ │ ├── test_module_http.py
│ │ │ │ ├── test_module_httpx.py
│ │ │ │ ├── test_module_hunt.py
│ │ │ │ ├── test_module_hunterio.py
│ │ │ │ ├── test_module_iis_shortnames.py
│ │ │ │ ├── test_module_ip2location.py
│ │ │ │ ├── test_module_ipneighbor.py
│ │ │ │ ├── test_module_ipstack.py
│ │ │ │ ├── test_module_jadx.py
│ │ │ │ ├── test_module_json.py
│ │ │ │ ├── test_module_leakix.py
│ │ │ │ ├── test_module_legba.py
│ │ │ │ ├── test_module_lightfuzz.py
│ │ │ │ ├── test_module_medusa.py
│ │ │ │ ├── test_module_mysql.py
│ │ │ │ ├── test_module_myssl.py
│ │ │ │ ├── test_module_neo4j.py
│ │ │ │ ├── test_module_newsletters.py
│ │ │ │ ├── test_module_nmap_xml.py
│ │ │ │ ├── test_module_ntlm.py
│ │ │ │ ├── test_module_nuclei.py
│ │ │ │ ├── test_module_oauth.py
│ │ │ │ ├── test_module_otx.py
│ │ │ │ ├── test_module_paramminer_cookies.py
│ │ │ │ ├── test_module_paramminer_getparams.py
│ │ │ │ ├── test_module_paramminer_headers.py
│ │ │ │ ├── test_module_passivetotal.py
│ │ │ │ ├── test_module_pgp.py
│ │ │ │ ├── test_module_portfilter.py
│ │ │ │ ├── test_module_portscan.py
│ │ │ │ ├── test_module_postgres.py
│ │ │ │ ├── test_module_postman.py
│ │ │ │ ├── test_module_postman_download.py
│ │ │ │ ├── test_module_python.py
│ │ │ │ ├── test_module_rapiddns.py
│ │ │ │ ├── test_module_reflected_parameters.py
│ │ │ │ ├── test_module_retirejs.py
│ │ │ │ ├── test_module_robots.py
│ │ │ │ ├── test_module_securitytrails.py
│ │ │ │ ├── test_module_securitytxt.py
│ │ │ │ ├── test_module_shodan_dns.py
│ │ │ │ ├── test_module_shodan_idb.py
│ │ │ │ ├── test_module_sitedossier.py
│ │ │ │ ├── test_module_skymem.py
│ │ │ │ ├── test_module_slack.py
│ │ │ │ ├── test_module_smuggler.py
│ │ │ │ ├── test_module_social.py
│ │ │ │ ├── test_module_speculate.py
│ │ │ │ ├── test_module_splunk.py
│ │ │ │ ├── test_module_sqlite.py
│ │ │ │ ├── test_module_sslcert.py
│ │ │ │ ├── test_module_stdout.py
│ │ │ │ ├── test_module_subdomaincenter.py
│ │ │ │ ├── test_module_subdomainradar.py
│ │ │ │ ├── test_module_subdomains.py
│ │ │ │ ├── test_module_teams.py
│ │ │ │ ├── test_module_telerik.py
│ │ │ │ ├── test_module_trickest.py
│ │ │ │ ├── test_module_trufflehog.py
│ │ │ │ ├── test_module_txt.py
│ │ │ │ ├── test_module_unarchive.py
│ │ │ │ ├── test_module_url_manipulation.py
│ │ │ │ ├── test_module_urlscan.py
│ │ │ │ ├── test_module_vhost.py
│ │ │ │ ├── test_module_viewdns.py
│ │ │ │ ├── test_module_virustotal.py
│ │ │ │ ├── test_module_wafw00f.py
│ │ │ │ ├── test_module_wayback.py
│ │ │ │ ├── test_module_web_parameters.py
│ │ │ │ ├── test_module_web_report.py
│ │ │ │ ├── test_module_websocket.py
│ │ │ │ └── test_module_wpscan.py
│ │ │ └── template_tests/
│ │ │ ├── __init__.py
│ │ │ └── test_template_subdomain_enum.py
│ │ ├── testsslcert.pem
│ │ └── testsslkey.pem
│ └── wordlists/
│ ├── devops_mutations.txt
│ ├── ms_on_prem_subdomains.txt
│ ├── nameservers.txt
│ ├── paramminer_headers.txt
│ ├── paramminer_parameters.txt
│ ├── raft-small-extensions-lowercase_CLEANED.txt
│ ├── top_open_ports_nmap.txt
│ └── valid_url_schemes.txt
├── bbot-docker.sh
├── codecov.yml
├── docs/
│ ├── comparison.md
│ ├── contribution.md
│ ├── data/
│ │ └── chord_graph/
│ │ ├── entities.json
│ │ ├── rels.json
│ │ └── vega.json
│ ├── dev/
│ │ ├── architecture.md
│ │ ├── basemodule.md
│ │ ├── core.md
│ │ ├── dev_environment.md
│ │ ├── discord_bot.md
│ │ ├── engine.md
│ │ ├── event.md
│ │ ├── helpers/
│ │ │ ├── command.md
│ │ │ ├── dns.md
│ │ │ ├── index.md
│ │ │ ├── interactsh.md
│ │ │ ├── misc.md
│ │ │ ├── web.md
│ │ │ └── wordcloud.md
│ │ ├── index.md
│ │ ├── module_howto.md
│ │ ├── presets.md
│ │ ├── scanner.md
│ │ ├── target.md
│ │ └── tests.md
│ ├── diagrams/
│ │ ├── engine-architecture.drawio
│ │ ├── event-flow.drawio
│ │ └── module-recursion.drawio
│ ├── how_it_works.md
│ ├── index.md
│ ├── javascripts/
│ │ ├── tablesort.js
│ │ ├── vega-embed@6.js
│ │ ├── vega-lite@5.js
│ │ └── vega@5.js
│ ├── modules/
│ │ ├── custom_yara_rules.md
│ │ ├── internal_modules.md
│ │ ├── lightfuzz.md
│ │ ├── list_of_modules.md
│ │ └── nuclei.md
│ ├── release_history.md
│ ├── scanning/
│ │ ├── advanced.md
│ │ ├── configuration.md
│ │ ├── events.md
│ │ ├── index.md
│ │ ├── output.md
│ │ ├── presets.md
│ │ ├── presets_list.md
│ │ └── tips_and_tricks.md
│ └── troubleshooting.md
├── examples/
│ └── discord_bot.py
├── extra_sass/
│ └── style.css.scss
├── funding.yml
├── mkdocs.yml
└── pyproject.toml
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
* text=auto
*.py text eol=lf
*.conf text eol=lf
*.txt text eol=lf
*.json text eol=lf
*.md text eol=lf
*.sh text eol=lf
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug Report
about: Create a report to help us improve
title: ""
labels: bug
assignees: ""
---
**Describe the bug**
What happened vs what was expected?
**BBOT Command**
Example: `bbot -m httpx -t evilcorp.com`
**OS, BBOT Installation Method + Version**
Example: `OS: Arch Linux, Installation method: pip, BBOT version: 1.0.3.545`
Note: You can get the BBOT version with `bbot --version`
Note: BBOT is designed from the ground up to run on Linux. Windows and MacOS are not officially supported. If you are using one of these platforms, it's recommended to use Docker.
**BBOT Config**
Attach your full BBOT preset (to show it, add `--current-preset` to your BBOT command).
**Logs/Screenshots**
If possible, produce the bug while `--debug` is enabled, and attach the relevant parts of the output.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature Request
about: Request a new feature
title: ""
labels: enhancement
assignees: ""
---
**Description**
Which feature would you like to see added to BBOT? What are its use cases?
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
target-branch: "dev"
open-pull-requests-limit: 10
- package-ecosystem: github-actions
directory: /
groups:
github-actions:
patterns:
- "*" # Group all Actions updates into a single larger pull request
schedule:
interval: weekly
target-branch: "dev"
================================================
FILE: .github/workflows/benchmark.yml
================================================
name: Performance Benchmarks
on:
pull_request:
paths:
- 'bbot/**/*.py'
- 'pyproject.toml'
- '.github/workflows/benchmark.yml'
concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
pull-requests: write
jobs:
benchmark:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0 # Need full history for branch comparison
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.11"
- name: Install dependencies
run: |
pip install poetry
poetry install --with dev
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libmagic1
# Generate benchmark comparison report using our branch-based script
- name: Generate benchmark comparison report
run: |
poetry run python bbot/scripts/benchmark_report.py \
--base ${{ github.base_ref }} \
--current ${{ github.head_ref }} \
--output benchmark_report.md \
--keep-results
continue-on-error: true
# Upload benchmark results as artifacts
- name: Upload benchmark results
uses: actions/upload-artifact@v6
with:
name: benchmark-results
path: |
benchmark_report.md
base_benchmark_results.json
current_benchmark_results.json
retention-days: 30
# Comment on PR with benchmark results
- name: Comment benchmark results on PR
uses: actions/github-script@v8
with:
script: |
const fs = require('fs');
try {
const report = fs.readFileSync('benchmark_report.md', 'utf8');
// Find existing benchmark comment (with pagination)
const comments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
per_page: 100, // Get more comments per page
});
// Debug: log all comments to see what we're working with
console.log(`Found ${comments.data.length} comments on this PR`);
comments.data.forEach((comment, index) => {
console.log(`Comment ${index}: user=${comment.user.login}, body preview="${comment.body.substring(0, 100)}..."`);
});
const existingComments = comments.data.filter(comment =>
comment.body.toLowerCase().includes('performance benchmark') &&
comment.user.login === 'github-actions[bot]'
);
console.log(`Found ${existingComments.length} existing benchmark comments`);
if (existingComments.length > 0) {
// Sort comments by creation date to find the most recent
const sortedComments = existingComments.sort((a, b) =>
new Date(b.created_at) - new Date(a.created_at)
);
const mostRecentComment = sortedComments[0];
console.log(`Updating most recent benchmark comment: ${mostRecentComment.id} (created: ${mostRecentComment.created_at})`);
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: mostRecentComment.id,
body: report
});
console.log('Updated existing benchmark comment');
// Delete any older duplicate comments
if (existingComments.length > 1) {
console.log(`Deleting ${existingComments.length - 1} older duplicate comments`);
for (let i = 1; i < sortedComments.length; i++) {
const commentToDelete = sortedComments[i];
console.log(`Attempting to delete comment ${commentToDelete.id} (created: ${commentToDelete.created_at})`);
try {
await github.rest.issues.deleteComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: commentToDelete.id
});
console.log(`Successfully deleted duplicate comment: ${commentToDelete.id}`);
} catch (error) {
console.error(`Failed to delete comment ${commentToDelete.id}: ${error.message}`);
console.error(`Error details:`, error);
}
}
}
} else {
// Create new comment
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: report
});
console.log('Created new benchmark comment');
}
} catch (error) {
console.error('Failed to post benchmark results:', error);
// Post a fallback comment
const fallbackMessage = [
'## Performance Benchmark Report',
'',
'> ⚠️ **Failed to generate detailed benchmark comparison**',
'> ',
'> The benchmark comparison failed to run. This might be because:',
'> - Benchmark tests don\'t exist on the base branch yet',
'> - Dependencies are missing',
'> - Test execution failed',
'> ',
'> Please check the [workflow logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details.',
'> ',
'> 📁 Benchmark artifacts may be available for download from the workflow run.'
].join('\\n');
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: fallbackMessage
});
}
================================================
FILE: .github/workflows/codeql.yml
================================================
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL Advanced"
on:
push:
branches: [ "stable", "dev" ]
pull_request:
branches: [ "stable", "dev" ]
schedule:
- cron: '32 3 * * 5'
jobs:
analyze:
name: Analyze (${{ matrix.language }})
# Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners (GitHub.com only)
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
permissions:
# required for all workflows
security-events: write
# required to fetch internal or private CodeQL packs
packages: read
# only required for workflows in private repositories
actions: read
contents: read
strategy:
fail-fast: false
matrix:
include:
- language: python
build-mode: none
# CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
# Use `c-cpp` to analyze code written in C, C++ or both
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: Checkout repository
uses: actions/checkout@v6
# Add any setup steps before running the `github/codeql-action/init` action.
# This includes steps like installing compilers or runtimes (`actions/setup-node`
# or others). This is typically only required for manual builds.
# - name: Setup runtime (example)
# uses: actions/setup-example@v1
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# If the analyze step fails for one of the languages you are analyzing with
# "We were unable to automatically build your code", modify the matrix above
# to set the build mode to "manual" for that language. Then modify this step
# to build your code.
# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
- if: matrix.build-mode == 'manual'
shell: bash
run: |
echo 'If you are using a "manual" build mode for one or more of the' \
'languages you are analyzing, replace this with the commands to build' \
'your code, for example:'
echo ' make bootstrap'
echo ' make release'
exit 1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
with:
category: "/language:${{matrix.language}}"
================================================
FILE: .github/workflows/distro_tests.yml
================================================
name: Tests (Linux Distros)
on:
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
test-distros:
runs-on: ubuntu-latest
container:
image: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: ["ubuntu:22.04", "ubuntu:24.04", "debian", "archlinux", "fedora", "kalilinux/kali-rolling", "parrotsec/security"]
steps:
- uses: actions/checkout@v6
- name: Install Python and Poetry
run: |
if [ -f /etc/os-release ]; then
. /etc/os-release
if [ "$ID" = "ubuntu" ] || [ "$ID" = "debian" ] || [ "$ID" = "kali" ] || [ "$ID" = "parrotsec" ]; then
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get -y install curl git bash build-essential docker.io libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev
elif [ "$ID" = "alpine" ]; then
apk add --no-cache bash gcc g++ musl-dev libffi-dev docker curl git make openssl-dev bzip2-dev zlib-dev xz-dev sqlite-dev
elif [ "$ID" = "arch" ]; then
pacman -Syu --noconfirm curl docker git bash base-devel
elif [ "$ID" = "fedora" ]; then
dnf install -y curl docker git bash gcc make patch p7zip p7zip-plugins openssl-devel bzip2-devel libffi-devel zlib-devel xz-devel tk-devel gdbm-devel readline-devel sqlite-devel python3-libdnf5
elif [ "$ID" = "gentoo" ]; then
echo "media-libs/libglvnd X" >> /etc/portage/package.use/libglvnd
emerge-webrsync
emerge --update --newuse dev-vcs/git media-libs/mesa curl docker bash
fi
fi
# Re-run the script with bash
exec bash -c "
curl https://pyenv.run | bash
export PATH=\"$HOME/.pyenv/bin:\$PATH\"
export PATH=\"$HOME/.local/bin:\$PATH\"
eval \"\$(pyenv init --path)\"
eval \"\$(pyenv init -)\"
eval \"\$(pyenv virtualenv-init -)\"
pyenv install 3.11
pyenv global 3.11
pyenv rehash
python3.11 -m pip install --user pipx
python3.11 -m pipx ensurepath
pipx install poetry
"
- name: Set OS Environment Variable
run: echo "OS_NAME=${{ matrix.os }}" | sed 's|[:/]|_|g' >> $GITHUB_ENV
- name: Run tests
run: |
export PATH="$HOME/.local/bin:$PATH"
export PATH="$HOME/.pyenv/bin:$PATH"
export PATH="$HOME/.pyenv/shims:$PATH"
export BBOT_DISTRO_TESTS=true
poetry env use python3.11
poetry install
poetry run pytest --reruns 2 --exitfirst -o timeout_func_only=true --timeout 1200 --disable-warnings --log-cli-level=INFO .
- name: Upload Debug Logs
if: always()
uses: actions/upload-artifact@v6
with:
name: pytest-debug-logs-${{ env.OS_NAME }}
path: pytest_debug.log
================================================
FILE: .github/workflows/docs_updater.yml
================================================
name: Daily Docs Update
on:
schedule:
- cron: '30 2 * * *' # Runs daily at 2:30 AM UTC, a less congested time
workflow_dispatch: # Allows manual triggering of the workflow
jobs:
update_docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
token: ${{ secrets.BBOT_DOCS_UPDATER_PAT }}
ref: dev # Checkout the dev branch
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.x"
- name: Install dependencies
run: |
pip install poetry
poetry install
- name: Generate docs
run: |
poetry run bbot/scripts/docs.py
- name: Create or Update Pull Request
uses: peter-evans/create-pull-request@v8
with:
token: ${{ secrets.BBOT_DOCS_UPDATER_PAT }}
branch: update-docs
base: dev
title: "Automated Docs Update"
body: "This is an automated pull request to update the documentation."
================================================
FILE: .github/workflows/tests.yml
================================================
name: Tests
on:
push:
branches:
- stable
- dev
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
strategy:
# if one python version fails, let the others finish
fail-fast: false
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Set Python Version Environment Variable
run: echo "PYTHON_VERSION=${{ matrix.python-version }}" | sed 's|[:/]|_|g' >> $GITHUB_ENV
- name: Install dependencies
run: |
pip install poetry
poetry install
- name: Lint
run: |
poetry run ruff check
poetry run ruff format --check
- name: Run tests
run: |
poetry run pytest -vv --reruns 2 -o timeout_func_only=true --timeout 1200 --disable-warnings --log-cli-level=INFO --cov-config=bbot/test/coverage.cfg --cov-report xml:cov.xml --cov=bbot .
- name: Upload Debug Logs
if: always()
uses: actions/upload-artifact@v6
with:
name: pytest-debug-logs-${{ env.PYTHON_VERSION }}
path: pytest_debug.log
- name: Upload Code Coverage
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./cov.xml
verbose: true
publish_code:
needs: test
runs-on: ubuntu-latest
if: github.event_name == 'push' && (github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/stable')
continue-on-error: true
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.x"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install poetry build
poetry self add "poetry-dynamic-versioning[plugin]"
- name: Build Pypi package
if: github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/dev'
run: python -m build
- name: Publish Pypi package
if: github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/dev'
uses: pypa/gh-action-pypi-publish@release/v1.13
with:
password: ${{ secrets.PYPI_API_TOKEN }}
- name: Get BBOT version
id: version
run: |
FULL_VERSION=$(poetry version | cut -d' ' -f2)
echo "BBOT_VERSION=$FULL_VERSION" >> $GITHUB_OUTPUT
# Extract major.minor (e.g., 2.7 from 2.7.1)
MAJOR_MINOR=$(echo "$FULL_VERSION" | cut -d'.' -f1-2)
echo "BBOT_VERSION_MAJOR_MINOR=$MAJOR_MINOR" >> $GITHUB_OUTPUT
# Extract major (e.g., 2 from 2.7.1)
MAJOR=$(echo "$FULL_VERSION" | cut -d'.' -f1)
echo "BBOT_VERSION_MAJOR=$MAJOR" >> $GITHUB_OUTPUT
- name: Publish to Docker Hub (dev)
if: github.event_name == 'push' && github.ref == 'refs/heads/dev'
uses: docker/build-push-action@v6
with:
push: true
context: .
tags: |
blacklanternsecurity/bbot:latest
blacklanternsecurity/bbot:dev
blacklanternsecurity/bbot:${{ steps.version.outputs.BBOT_VERSION }}
blacklanternsecurity/bbot:${{ steps.version.outputs.BBOT_VERSION_MAJOR_MINOR }}
blacklanternsecurity/bbot:${{ steps.version.outputs.BBOT_VERSION_MAJOR }}
- name: Publish to Docker Hub (stable)
if: github.event_name == 'push' && github.ref == 'refs/heads/stable'
uses: docker/build-push-action@v6
with:
push: true
context: .
tags: |
blacklanternsecurity/bbot:stable
blacklanternsecurity/bbot:${{ steps.version.outputs.BBOT_VERSION }}
blacklanternsecurity/bbot:${{ steps.version.outputs.BBOT_VERSION_MAJOR_MINOR }}
blacklanternsecurity/bbot:${{ steps.version.outputs.BBOT_VERSION_MAJOR }}
- name: Publish Full Docker Image to Docker Hub (dev)
if: github.event_name == 'push' && github.ref == 'refs/heads/dev'
uses: docker/build-push-action@v6
with:
push: true
file: Dockerfile.full
context: .
tags: |
blacklanternsecurity/bbot:latest-full
blacklanternsecurity/bbot:dev-full
blacklanternsecurity/bbot:${{ steps.version.outputs.BBOT_VERSION }}-full
blacklanternsecurity/bbot:${{ steps.version.outputs.BBOT_VERSION_MAJOR_MINOR }}-full
blacklanternsecurity/bbot:${{ steps.version.outputs.BBOT_VERSION_MAJOR }}-full
- name: Publish Full Docker Image to Docker Hub (stable)
if: github.event_name == 'push' && github.ref == 'refs/heads/stable'
uses: docker/build-push-action@v6
with:
push: true
file: Dockerfile.full
context: .
tags: |
blacklanternsecurity/bbot:stable-full
blacklanternsecurity/bbot:${{ steps.version.outputs.BBOT_VERSION }}-full
blacklanternsecurity/bbot:${{ steps.version.outputs.BBOT_VERSION_MAJOR_MINOR }}-full
blacklanternsecurity/bbot:${{ steps.version.outputs.BBOT_VERSION_MAJOR }}-full
- name: Docker Hub Description
if: github.event_name == 'push' && github.ref == 'refs/heads/dev'
uses: peter-evans/dockerhub-description@v5
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
repository: blacklanternsecurity/bbot
- name: Clean up old Docker Hub tags (up to 50 most recent tags plus 'latest')
if: github.event_name == 'push' && github.ref == 'refs/heads/dev'
run: |
# Install jq for JSON processing
sudo apt-get update && sudo apt-get install -y jq
IMAGE="blacklanternsecurity/bbot"
# Clean up dev tags (keep 50 most recent)
for tag_pattern in "rc$" "rc-full$"; do
echo "Cleaning up tags ending with $tag_pattern..."
tags_response=$(curl -s -H "Authorization: Bearer ${{ secrets.DOCKER_TOKEN }}" \
"https://hub.docker.com/v2/repositories/$IMAGE/tags/?page_size=100")
tags_to_delete=$(echo "$tags_response" | jq -r --arg pattern "$tag_pattern" \
'.results[] | select(.name | test($pattern)) | [.last_updated, .name] | @tsv' | \
sort -r | tail -n +51 | cut -f2)
for tag in $tags_to_delete; do
echo "Deleting $IMAGE tag: $tag"
curl -X DELETE -H "Authorization: Bearer ${{ secrets.DOCKER_TOKEN }}" \
"https://hub.docker.com/v2/repositories/$IMAGE/tags/$tag/"
done
echo "Cleanup completed for tags ending with $tag_pattern. Kept 50 most recent."
done
outputs:
BBOT_VERSION: ${{ steps.version.outputs.BBOT_VERSION }}
publish_docs:
runs-on: ubuntu-latest
if: github.event_name == 'push' && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/dev')
steps:
- uses: actions/checkout@v6
with:
token: ${{ secrets.BBOT_DOCS_UPDATER_PAT }}
- uses: actions/setup-python@v6
with:
python-version: "3.11"
- run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
- uses: actions/cache@v5
with:
key: mkdocs-material-${{ env.cache_id }}
path: .cache
restore-keys: |
mkdocs-material-
- name: Install dependencies
run: |
pip install poetry
poetry install --only=docs
- name: Configure Git
run: |
git config user.name github-actions
git config user.email github-actions@github.com
git fetch origin gh-pages:refs/remotes/origin/gh-pages
if git show-ref --verify --quiet refs/heads/gh-pages; then
git branch -f gh-pages origin/gh-pages
else
git branch --track gh-pages origin/gh-pages
fi
- name: Generate docs (stable branch)
if: github.ref == 'refs/heads/stable'
run: |
poetry run mike deploy Stable
- name: Generate docs (dev branch)
if: github.ref == 'refs/heads/dev'
run: |
poetry run mike deploy Dev
- name: Publish docs
run: |
git switch gh-pages
git push
# tag_commit:
# needs: publish_code
# runs-on: ubuntu-latest
# if: github.event_name == 'push' && github.ref == 'refs/heads/stable'
# steps:
# - uses: actions/checkout@v6
# with:
# ref: ${{ github.head_ref }}
# fetch-depth: 0 # Fetch all history for all tags and branches
# - name: Configure git
# run: |
# git config --local user.email "info@blacklanternsecurity.com"
# git config --local user.name "GitHub Actions"
# - name: Tag commit
# run: |
# VERSION="${{ needs.publish_code.outputs.BBOT_VERSION }}"
# if [[ "${{ github.ref }}" == "refs/heads/dev" ]]; then
# TAG_MESSAGE="Dev Release $VERSION"
# elif [[ "${{ github.ref }}" == "refs/heads/stable" ]]; then
# TAG_MESSAGE="Stable Release $VERSION"
# fi
# git tag -a $VERSION -m "$TAG_MESSAGE"
# git push origin --tags
================================================
FILE: .github/workflows/version_updater.yml
================================================
name: Version Updater
on:
schedule:
# Runs at 00:00 every day
- cron: '0 0 * * *'
workflow_dispatch: # Adds the ability to manually trigger the workflow
jobs:
update-nuclei-version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
ref: dev
fetch-depth: 0
token: ${{ secrets.BBOT_DOCS_UPDATER_PAT }}
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install requests
- name: Get latest version
id: get-latest-version
run: |
import os, requests
response = requests.get('https://api.github.com/repos/projectdiscovery/nuclei/releases/latest')
version = response.json()['tag_name'].lstrip('v')
release_notes = response.json()['body']
with open(os.getenv('GITHUB_ENV'), 'a') as env_file:
env_file.write(f"latest_version={version}\n")
env_file.write(f"release_notes<<EOF\n{release_notes}\nEOF\n")
shell: python
- name: Get current version
id: get-current-version
run: |
version=$(grep -m 1 -oP '(?<=version": ")[^"]*' bbot/modules/nuclei.py)
echo "current_version=$version" >> $GITHUB_ENV
- name: Update version
id: update-version
if: env.latest_version != env.current_version
run: "sed -i '0,/\"version\": \".*\",/ s/\"version\": \".*\",/\"version\": \"${{ env.latest_version }}\",/g' bbot/modules/nuclei.py"
- name: Create pull request to update the version
if: steps.update-version.outcome == 'success'
uses: peter-evans/create-pull-request@v8
with:
token: ${{ secrets.BBOT_DOCS_UPDATER_PAT }}
commit-message: "Update nuclei"
title: "Update nuclei to ${{ env.latest_version }}"
body: |
This PR uses https://api.github.com/repos/projectdiscovery/nuclei/releases/latest to obtain the latest version of nuclei and update the version in bbot/modules/nuclei.py."
# Release notes:
${{ env.release_notes }}
branch: "update-nuclei"
committer: blsaccess <info@blacklanternsecurity.com>
author: blsaccess <info@blacklanternsecurity.com>
assignees: "TheTechromancer"
update-trufflehog-version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
ref: dev
fetch-depth: 0
token: ${{ secrets.BBOT_DOCS_UPDATER_PAT }}
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install requests
- name: Get latest version
id: get-latest-version
run: |
import os, requests
response = requests.get('https://api.github.com/repos/trufflesecurity/trufflehog/releases/latest')
version = response.json()['tag_name'].lstrip('v')
release_notes = response.json()['body']
with open(os.getenv('GITHUB_ENV'), 'a') as env_file:
env_file.write(f"latest_version={version}\n")
env_file.write(f"release_notes<<EOF\n{release_notes}\nEOF\n")
shell: python
- name: Get current version
id: get-current-version
run: |
version=$(grep -m 1 -oP '(?<=version": ")[^"]*' bbot/modules/trufflehog.py)
echo "current_version=$version" >> $GITHUB_ENV
- name: Update version
id: update-version
if: env.latest_version != env.current_version
run: "sed -i '0,/\"version\": \".*\",/ s/\"version\": \".*\",/\"version\": \"${{ env.latest_version }}\",/g' bbot/modules/trufflehog.py"
- name: Create pull request to update the version
if: steps.update-version.outcome == 'success'
uses: peter-evans/create-pull-request@v8
with:
token: ${{ secrets.BBOT_DOCS_UPDATER_PAT }}
commit-message: "Update trufflehog"
title: "Update trufflehog to ${{ env.latest_version }}"
body: |
This PR uses https://api.github.com/repos/trufflesecurity/trufflehog/releases/latest to obtain the latest version of trufflehog and update the version in bbot/modules/trufflehog.py.
# Release notes:
${{ env.release_notes }}
branch: "update-trufflehog"
committer: blsaccess <info@blacklanternsecurity.com>
author: blsaccess <info@blacklanternsecurity.com>
assignees: "TheTechromancer"
================================================
FILE: .gitignore
================================================
__pycache__/
.coverage*
/data/
/neo4j/
================================================
FILE: .gitmodules
================================================
[submodule "bbot/modules/playground"]
path = bbot/modules/playground
url = https://github.com/blacklanternsecurity/bbot-module-playground
branch = main
================================================
FILE: .pre-commit-config.yaml
================================================
# Learn more about this config here: https://pre-commit.com/
# To enable these pre-commit hooks run:
# `pipx install pre-commit` or `brew install pre-commit`
# Then in the project root directory run `pre-commit install`
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: check-added-large-files
- id: check-ast
- id: check-builtin-literals
- id: check-byte-order-marker
- id: check-case-conflict
# - id: check-docstring-first
# - id: check-executables-have-shebangs
- id: check-json
- id: check-merge-conflict
# - id: check-shebang-scripts-are-executable
- id: check-symlinks
- id: check-toml
- id: check-vcs-permalinks
- id: check-xml
# - id: check-yaml
- id: debug-statements
- id: destroyed-symlinks
# - id: detect-private-key
- id: end-of-file-fixer
- id: file-contents-sorter
- id: fix-byte-order-marker
- id: forbid-new-submodules
- id: forbid-submodules
- id: mixed-line-ending
- id: requirements-txt-fixer
- id: sort-simple-yaml
- id: trailing-whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.0
hooks:
- id: ruff
- id: ruff-format
- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.23
hooks:
- id: validate-pyproject
================================================
FILE: Dockerfile
================================================
FROM python:3.11-slim
ENV LANG=C.UTF-8
ENV LC_ALL=C.UTF-8
ENV PIP_NO_CACHE_DIR=off
WORKDIR /usr/src/bbot
RUN apt-get update && apt-get install -y openssl gcc git make unzip curl wget vim nano sudo
COPY . .
RUN pip install .
WORKDIR /root
ENTRYPOINT [ "bbot" ]
================================================
FILE: Dockerfile.full
================================================
FROM python:3.11-slim
ENV LANG=C.UTF-8
ENV LC_ALL=C.UTF-8
ENV PIP_NO_CACHE_DIR=off
WORKDIR /usr/src/bbot
RUN apt-get update && apt-get install -y openssl gcc git make unzip curl wget vim nano sudo
COPY . .
RUN pip install .
RUN bbot --install-all-deps
WORKDIR /root
ENTRYPOINT [ "bbot" ]
================================================
FILE: LICENSE
================================================
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.
================================================
FILE: README.md
================================================
[](https://github.com/blacklanternsecurity/bbot)
[](https://www.python.org) [](https://github.com/blacklanternsecurity/bbot/blob/dev/LICENSE) [](https://www.reconvillage.org/talks) [](https://pepy.tech/project/bbot) [](https://github.com/astral-sh/ruff) [](https://github.com/blacklanternsecurity/bbot/actions?query=workflow%3A"tests") [](https://codecov.io/gh/blacklanternsecurity/bbot) [](https://discord.com/invite/PZqkgxu5SA)
### **BEE·bot** is a multipurpose scanner inspired by [Spiderfoot](https://github.com/smicallef/spiderfoot), built to automate your **Recon**, **Bug Bounties**, and **ASM**!
https://github.com/blacklanternsecurity/bbot/assets/20261699/e539e89b-92ea-46fa-b893-9cde94eebf81
_A BBOT scan in real-time - visualization with [VivaGraphJS](https://github.com/blacklanternsecurity/bbot-vivagraphjs)_
## Installation
```bash
# stable version
pipx install bbot
# bleeding edge (dev branch)
pipx install --pip-args '\--pre' bbot
```
_For more installation methods, including [Docker](https://hub.docker.com/r/blacklanternsecurity/bbot), see [Getting Started](https://www.blacklanternsecurity.com/bbot/Stable/)_
## Example Commands
### 1) Subdomain Finder
Passive API sources plus a recursive DNS brute-force with target-specific subdomain mutations.
```bash
# find subdomains of evilcorp.com
bbot -t evilcorp.com -p subdomain-enum
# passive sources only
bbot -t evilcorp.com -p subdomain-enum -rf passive
```
<!-- BBOT SUBDOMAIN-ENUM PRESET EXPANDABLE -->
<details>
<summary><b><code>subdomain-enum.yml</code></b></summary>
```yaml
description: Enumerate subdomains via APIs, brute-force
flags:
# enable every module with the subdomain-enum flag
- subdomain-enum
output_modules:
# output unique subdomains to TXT file
- subdomains
config:
dns:
threads: 25
brute_threads: 1000
# put your API keys here
# modules:
# github:
# api_key: ""
# chaos:
# api_key: ""
# securitytrails:
# api_key: ""
```
</details>
<!-- END BBOT SUBDOMAIN-ENUM PRESET EXPANDABLE -->
BBOT consistently finds 20-50% more subdomains than other tools. The bigger the domain, the bigger the difference. To learn how this is possible, see [How It Works](https://www.blacklanternsecurity.com/bbot/Dev/how_it_works/).

### 2) Web Spider
```bash
# crawl evilcorp.com, extracting emails and other goodies
bbot -t evilcorp.com -p spider
```
<!-- BBOT SPIDER PRESET EXPANDABLE -->
<details>
<summary><b><code>spider.yml</code></b></summary>
```yaml
description: Recursive web spider
modules:
- httpx
blacklist:
# Prevent spider from invalidating sessions by logging out
- "RE:/.*(sign|log)[_-]?out"
config:
web:
# how many links to follow in a row
spider_distance: 2
# don't follow links whose directory depth is higher than 4
spider_depth: 4
# maximum number of links to follow per page
spider_links_per_page: 25
```
</details>
<!-- END BBOT SPIDER PRESET EXPANDABLE -->
### 3) Email Gatherer
```bash
# quick email enum with free APIs + scraping
bbot -t evilcorp.com -p email-enum
# pair with subdomain enum + web spider for maximum yield
bbot -t evilcorp.com -p email-enum subdomain-enum spider
```
<!-- BBOT EMAIL-ENUM PRESET EXPANDABLE -->
<details>
<summary><b><code>email-enum.yml</code></b></summary>
```yaml
description: Enumerate email addresses from APIs, web crawling, etc.
flags:
- email-enum
output_modules:
- emails
```
</details>
<!-- END BBOT EMAIL-ENUM PRESET EXPANDABLE -->
### 4) Web Scanner
```bash
# run a light web scan against www.evilcorp.com
bbot -t www.evilcorp.com -p web-basic
# run a heavy web scan against www.evilcorp.com
bbot -t www.evilcorp.com -p web-thorough
```
<!-- BBOT WEB-BASIC PRESET EXPANDABLE -->
<details>
<summary><b><code>web-basic.yml</code></b></summary>
```yaml
description: Quick web scan
include:
- iis-shortnames
flags:
- web-basic
```
</details>
<!-- END BBOT WEB-BASIC PRESET EXPANDABLE -->
<!-- BBOT WEB-THOROUGH PRESET EXPANDABLE -->
<details>
<summary><b><code>web-thorough.yml</code></b></summary>
```yaml
description: Aggressive web scan
include:
# include the web-basic preset
- web-basic
flags:
- web-thorough
```
</details>
<!-- END BBOT WEB-THOROUGH PRESET EXPANDABLE -->
### 5) Everything Everywhere All at Once
```bash
# everything everywhere all at once
bbot -t evilcorp.com -p kitchen-sink --allow-deadly
# roughly equivalent to:
bbot -t evilcorp.com -p subdomain-enum cloud-enum code-enum email-enum spider web-basic paramminer dirbust-light web-screenshots --allow-deadly
```
<!-- BBOT KITCHEN-SINK PRESET EXPANDABLE -->
<details>
<summary><b><code>kitchen-sink.yml</code></b></summary>
```yaml
description: Everything everywhere all at once
include:
- subdomain-enum
- cloud-enum
- code-enum
- email-enum
- spider
- web-basic
- paramminer
- dirbust-light
- web-screenshots
- baddns-intense
config:
modules:
baddns:
enable_references: True
```
</details>
<!-- END BBOT KITCHEN-SINK PRESET EXPANDABLE -->
## How it Works
Click the graph below to explore the [inner workings](https://www.blacklanternsecurity.com/bbot/Stable/how_it_works/) of BBOT.
[](https://www.blacklanternsecurity.com/bbot/Stable/how_it_works/)
## Output Modules
- [Neo4j](docs/scanning/output.md#neo4j)
- [Teams](docs/scanning/output.md#teams)
- [Discord](docs/scanning/output.md#discord)
- [Slack](docs/scanning/output.md#slack)
- [Postgres](docs/scanning/output.md#postgres)
- [MySQL](docs/scanning/output.md#mysql)
- [SQLite](docs/scanning/output.md#sqlite)
- [Splunk](docs/scanning/output.md#splunk)
- [Elasticsearch](docs/scanning/output.md#elasticsearch)
- [CSV](docs/scanning/output.md#csv)
- [JSON](docs/scanning/output.md#json)
- [HTTP](docs/scanning/output.md#http)
- [Websocket](docs/scanning/output.md#websocket)
...and [more](docs/scanning/output.md)!
## BBOT as a Python Library
#### Synchronous
```python
from bbot.scanner import Scanner
if __name__ == "__main__":
scan = Scanner("evilcorp.com", presets=["subdomain-enum"])
for event in scan.start():
print(event)
```
#### Asynchronous
```python
from bbot.scanner import Scanner
async def main():
scan = Scanner("evilcorp.com", presets=["subdomain-enum"])
async for event in scan.async_start():
print(event.json())
if __name__ == "__main__":
import asyncio
asyncio.run(main())
```
<details>
<summary><b>SEE: This Nefarious Discord Bot</b></summary>
A [BBOT Discord Bot](https://www.blacklanternsecurity.com/bbot/Stable/dev/#discord-bot-example) that responds to the `/scan` command. Scan the internet from the comfort of your discord server!

</details>
## Feature Overview
- Support for Multiple Targets
- Web Screenshots
- Suite of Offensive Web Modules
- NLP-powered Subdomain Mutations
- Native Output to Neo4j (and more)
- Automatic dependency install with Ansible
- Search entire attack surface with custom YARA rules
- Python API + Developer Documentation
## Targets
BBOT accepts an unlimited number of targets via `-t`. You can specify targets either directly on the command line or in files (or both!):
```bash
bbot -t evilcorp.com evilcorp.org 1.2.3.0/24 -p subdomain-enum
```
Targets can be any of the following:
- DNS Name (`evilcorp.com`)
- IP Address (`1.2.3.4`)
- IP Range (`1.2.3.0/24`)
- Open TCP Port (`192.168.0.1:80`)
- URL (`https://www.evilcorp.com`)
- Email Address (`bob@evilcorp.com`)
- Organization (`ORG:evilcorp`)
- Username (`USER:bobsmith`)
- Filesystem (`FILESYSTEM:/tmp/asdf`)
- Mobile App (`MOBILE_APP:https://play.google.com/store/apps/details?id=com.evilcorp.app`)
For more information, see [Targets](https://www.blacklanternsecurity.com/bbot/Stable/scanning/#targets-t). To learn how BBOT handles scope, see [Scope](https://www.blacklanternsecurity.com/bbot/Stable/scanning/#scope).
## API Keys
Similar to Amass or Subfinder, BBOT supports API keys for various third-party services such as SecurityTrails, etc.
The standard way to do this is to enter your API keys in **`~/.config/bbot/bbot.yml`**. Note that multiple API keys are allowed:
```yaml
modules:
shodan_dns:
api_key: 4f41243847da693a4f356c0486114bc6
c99:
# multiple API keys
api_key:
- 21a270d5f59c9b05813a72bb41707266
- ea8f243d9885cf8ce9876a580224fd3c
- 5bc6ed268ab6488270e496d3183a1a27
virustotal:
api_key: dd5f0eee2e4a99b71a939bded450b246
securitytrails:
api_key: d9a05c3fd9a514497713c54b4455d0b0
```
If you like, you can also specify them on the command line:
```bash
bbot -c modules.virustotal.api_key=dd5f0eee2e4a99b71a939bded450b246
```
For details, see [Configuration](https://www.blacklanternsecurity.com/bbot/Stable/scanning/configuration/).
## Complete Lists of Modules, Flags, etc.
- Complete list of [Modules](https://www.blacklanternsecurity.com/bbot/Stable/modules/list_of_modules/).
- Complete list of [Flags](https://www.blacklanternsecurity.com/bbot/Stable/scanning/#list-of-flags).
- Complete list of [Presets](https://www.blacklanternsecurity.com/bbot/Stable/scanning/presets_list/).
- Complete list of [Global Config Options](https://www.blacklanternsecurity.com/bbot/Stable/scanning/configuration/#global-config-options).
- Complete list of [Module Config Options](https://www.blacklanternsecurity.com/bbot/Stable/scanning/configuration/#module-config-options).
## Documentation
<!-- BBOT DOCS TOC -->
- **User Manual**
- **Basics**
- [Getting Started](https://www.blacklanternsecurity.com/bbot/Stable/)
- [How it Works](https://www.blacklanternsecurity.com/bbot/Stable/how_it_works)
- [Comparison to Other Tools](https://www.blacklanternsecurity.com/bbot/Stable/comparison)
- **Scanning**
- [Scanning Overview](https://www.blacklanternsecurity.com/bbot/Stable/scanning/)
- **Presets**
- [Overview](https://www.blacklanternsecurity.com/bbot/Stable/scanning/presets)
- [List of Presets](https://www.blacklanternsecurity.com/bbot/Stable/scanning/presets_list)
- [Events](https://www.blacklanternsecurity.com/bbot/Stable/scanning/events)
- [Output](https://www.blacklanternsecurity.com/bbot/Stable/scanning/output)
- [Tips and Tricks](https://www.blacklanternsecurity.com/bbot/Stable/scanning/tips_and_tricks)
- [Advanced Usage](https://www.blacklanternsecurity.com/bbot/Stable/scanning/advanced)
- [Configuration](https://www.blacklanternsecurity.com/bbot/Stable/scanning/configuration)
- **Modules**
- [List of Modules](https://www.blacklanternsecurity.com/bbot/Stable/modules/list_of_modules)
- [Nuclei](https://www.blacklanternsecurity.com/bbot/Stable/modules/nuclei)
- [Custom YARA Rules](https://www.blacklanternsecurity.com/bbot/Stable/modules/custom_yara_rules)
- [Lightfuzz](https://www.blacklanternsecurity.com/bbot/Stable/modules/lightfuzz)
- **Misc**
- [Contribution](https://www.blacklanternsecurity.com/bbot/Stable/contribution)
- [Release History](https://www.blacklanternsecurity.com/bbot/Stable/release_history)
- [Troubleshooting](https://www.blacklanternsecurity.com/bbot/Stable/troubleshooting)
- **Developer Manual**
- [Development Overview](https://www.blacklanternsecurity.com/bbot/Stable/dev/)
- [Setting Up a Dev Environment](https://www.blacklanternsecurity.com/bbot/Stable/dev/dev_environment)
- [BBOT Internal Architecture](https://www.blacklanternsecurity.com/bbot/Stable/dev/architecture)
- [How to Write a BBOT Module](https://www.blacklanternsecurity.com/bbot/Stable/dev/module_howto)
- [Unit Tests](https://www.blacklanternsecurity.com/bbot/Stable/dev/tests)
- [Discord Bot Example](https://www.blacklanternsecurity.com/bbot/Stable/dev/discord_bot)
- **Code Reference**
- [Scanner](https://www.blacklanternsecurity.com/bbot/Stable/dev/scanner)
- [Presets](https://www.blacklanternsecurity.com/bbot/Stable/dev/presets)
- [Event](https://www.blacklanternsecurity.com/bbot/Stable/dev/event)
- [Target](https://www.blacklanternsecurity.com/bbot/Stable/dev/target)
- [BaseModule](https://www.blacklanternsecurity.com/bbot/Stable/dev/basemodule)
- [BBOTCore](https://www.blacklanternsecurity.com/bbot/Stable/dev/core)
- [Engine](https://www.blacklanternsecurity.com/bbot/Stable/dev/engine)
- **Helpers**
- [Overview](https://www.blacklanternsecurity.com/bbot/Stable/dev/helpers/)
- [Command](https://www.blacklanternsecurity.com/bbot/Stable/dev/helpers/command)
- [DNS](https://www.blacklanternsecurity.com/bbot/Stable/dev/helpers/dns)
- [Interactsh](https://www.blacklanternsecurity.com/bbot/Stable/dev/helpers/interactsh)
- [Miscellaneous](https://www.blacklanternsecurity.com/bbot/Stable/dev/helpers/misc)
- [Web](https://www.blacklanternsecurity.com/bbot/Stable/dev/helpers/web)
- [Word Cloud](https://www.blacklanternsecurity.com/bbot/Stable/dev/helpers/wordcloud)
<!-- END BBOT DOCS TOC -->
## Contribution
Some of the best BBOT modules were written by the community. BBOT is being constantly improved; every day it grows more powerful!
We welcome contributions. Not just code, but ideas too! If you have an idea for a new feature, please let us know in [Discussions](https://github.com/blacklanternsecurity/bbot/discussions). If you want to get your hands dirty, see [Contribution](https://www.blacklanternsecurity.com/bbot/Stable/contribution/). There you can find setup instructions and a simple tutorial on how to write a BBOT module. We also have extensive [Developer Documentation](https://www.blacklanternsecurity.com/bbot/Stable/dev/).
Thanks to these amazing people for contributing to BBOT! :heart:
<p align="center">
<a href="https://github.com/blacklanternsecurity/bbot/graphs/contributors">
<img src="https://contrib.rocks/image?repo=blacklanternsecurity/bbot&max=500">
</a>
</p>
Special thanks to:
- @TheTechromancer for creating BBOT
- @liquidsec for his extensive work on BBOT's web hacking features, including [badsecrets](https://github.com/blacklanternsecurity/badsecrets) and [baddns](https://github.com/blacklanternsecurity/baddns)
- Steve Micallef (@smicallef) for creating Spiderfoot
- @kerrymilan for his Neo4j and Ansible expertise
- @domwhewell-sage for his family of badass code-looting modules
- @aconite33 and @amiremami for their ruthless testing
- Aleksei Kornev (@alekseiko) for granting us ownership of the bbot Pypi repository <3
================================================
FILE: bbot/__init__.py
================================================
# version placeholder (replaced by poetry-dynamic-versioning)
__version__ = "v0.0.0"
from .scanner import Scanner, Preset
__all__ = ["Scanner", "Preset"]
================================================
FILE: bbot/cli.py
================================================
#!/usr/bin/env python3
import io
import sys
import logging
import multiprocessing
from bbot.errors import *
from bbot import __version__
from bbot.logger import log_to_stderr
from bbot.core.helpers.misc import chain_lists, rm_rf
if multiprocessing.current_process().name == "MainProcess":
silent = "-s" in sys.argv or "--silent" in sys.argv
if not silent:
ascii_art = rf""" [1;38;5;208m ______ [0m _____ ____ _______
[1;38;5;208m| ___ \[0m| __ \ / __ \__ __|
[1;38;5;208m| |___) [0m| |__) | | | | | |
[1;38;5;208m| ___ <[0m| __ <| | | | | |
[1;38;5;208m| |___) [0m| |__) | |__| | | |
[1;38;5;208m|______/[0m|_____/ \____/ |_|
[1;38;5;208mBIGHUGE[0m BLS OSINT TOOL {__version__}
www.blacklanternsecurity.com/bbot
"""
print(ascii_art, file=sys.stderr)
scan_name = ""
async def _main():
import asyncio
import traceback
from contextlib import suppress
# fix tee buffering
sys.stdout.reconfigure(line_buffering=True)
log = logging.getLogger("bbot.cli")
from bbot.scanner import Scanner
from bbot.scanner.preset import Preset
global scan_name
try:
# start by creating a default scan preset
preset = Preset(_log=True, name="bbot_cli_main")
# parse command line arguments and merge into preset
try:
preset.parse_args()
except BBOTArgumentError as e:
log_to_stderr(str(e), level="WARNING")
log.trace(traceback.format_exc())
return
# ensure arguments (-c config options etc.) are valid
options = preset.args.parsed
# print help if no arguments
if len(sys.argv) == 1:
print(preset.args.parser.format_help())
sys.exit(1)
return
# --version
if options.version:
print(__version__)
sys.exit(0)
return
# --list-presets
if options.list_presets:
print("")
print("### PRESETS ###")
print("")
for row in preset.presets_table().splitlines():
print(row)
return
# if we're listing modules or their options
if options.list_modules or options.list_output_modules or options.list_module_options or options.module_help:
# if no modules or flags are specified, enable everything
if not (options.modules or options.output_modules or options.flags):
for module, preloaded in preset.module_loader.preloaded().items():
module_type = preloaded.get("type", "scan")
preset.add_module(module, module_type=module_type)
if options.modules or options.output_modules or options.flags:
preset._default_output_modules = options.output_modules
preset._default_internal_modules = []
preset.bake()
# --list-modules
if options.list_modules:
print("")
print("### MODULES ###")
print("")
modules = sorted(set(preset.scan_modules + preset.internal_modules))
for row in preset.module_loader.modules_table(modules).splitlines():
print(row)
return
# --list-output-modules
if options.list_output_modules:
print("")
print("### OUTPUT MODULES ###")
print("")
for row in preset.module_loader.modules_table(preset.output_modules).splitlines():
print(row)
return
# --list-module-options
if options.list_module_options:
print("")
print("### MODULE OPTIONS ###")
print("")
for row in preset.module_loader.modules_options_table(preset.modules).splitlines():
print(row)
return
# --module-help
if options.module_help:
module_name = options.module_help
all_modules = list(preset.module_loader.preloaded())
if module_name not in all_modules:
log.hugewarning(f'Module "{module_name}" not found')
return
# Load the module class
loaded_modules = preset.module_loader.load_modules([module_name])
module_name, module_class = next(iter(loaded_modules.items()))
print(module_class.help_text())
return
# --list-flags
if options.list_flags:
flags = preset.flags if preset.flags else None
print("")
print("### FLAGS ###")
print("")
for row in preset.module_loader.flags_table(flags=flags).splitlines():
print(row)
return
try:
scan = Scanner(preset=preset)
except (PresetAbortError, ValidationError) as e:
log.warning(str(e))
return
deadly_modules = [
m for m in scan.preset.scan_modules if "deadly" in preset.preloaded_module(m).get("flags", [])
]
if deadly_modules and not options.allow_deadly:
log.hugewarning(f"You enabled the following deadly modules: {','.join(deadly_modules)}")
log.hugewarning("Deadly modules are highly intrusive")
log.hugewarning("Please specify --allow-deadly to continue")
return False
# --current-preset
if options.current_preset:
print(scan.preset.to_yaml())
sys.exit(0)
return
# --current-preset-full
if options.current_preset_full:
print(scan.preset.to_yaml(full_config=True))
sys.exit(0)
return
# --install-all-deps
if options.install_all_deps:
preloaded_modules = preset.module_loader.preloaded()
scan_modules = [k for k, v in preloaded_modules.items() if str(v.get("type", "")) == "scan"]
output_modules = [k for k, v in preloaded_modules.items() if str(v.get("type", "")) == "output"]
log.verbose("Creating dummy scan with all modules + output modules for deps installation")
dummy_scan = Scanner(preset=preset, modules=scan_modules, output_modules=output_modules)
dummy_scan.helpers.depsinstaller.force_deps = True
log.info("Installing module dependencies")
await dummy_scan.load_modules()
log.verbose("Running module setups")
succeeded, hard_failed, soft_failed = await dummy_scan.setup_modules(deps_only=True)
# remove any leftovers from the dummy scan
rm_rf(dummy_scan.home, ignore_errors=True)
rm_rf(dummy_scan.temp_dir, ignore_errors=True)
if succeeded:
log.success(
f"Successfully installed dependencies for {len(succeeded):,} modules: {','.join(succeeded)}"
)
if soft_failed or hard_failed:
failed = soft_failed + hard_failed
log.warning(f"Failed to install dependencies for {len(failed):,} modules: {', '.join(failed)}")
return False
return True
scan_name = str(scan.name)
log.verbose("")
log.verbose("### MODULES ENABLED ###")
log.verbose("")
for row in scan.preset.module_loader.modules_table(scan.preset.modules).splitlines():
log.verbose(row)
scan.helpers.word_cloud.load()
await scan._prep()
if not options.dry_run:
log.trace(f"Command: {' '.join(sys.argv)}")
if sys.stdin.isatty():
# warn if any targets belong directly to a cloud provider
if not scan.preset.strict_scope:
for event in scan.target.seeds.event_seeds:
if event.type == "DNS_NAME":
cloudcheck_result = await scan.helpers.cloudcheck.lookup(event.host)
if cloudcheck_result:
scan.hugewarning(
f'YOUR TARGET CONTAINS A CLOUD DOMAIN: "{event.host}". You\'re in for a wild ride!'
)
if not options.yes:
log.hugesuccess(f"Scan ready. Press enter to execute {scan.name}")
input()
import os
import re
import fcntl
from bbot.core.helpers.misc import smart_decode
def handle_keyboard_input(keyboard_input):
kill_regex = re.compile(r"kill (?P<modules>[a-z0-9_ ,]+)")
if keyboard_input:
log.verbose(f'Got keyboard input: "{keyboard_input}"')
kill_match = kill_regex.match(keyboard_input)
if kill_match:
modules = kill_match.group("modules")
if modules:
modules = chain_lists(modules)
for module in modules:
if module in scan.modules:
log.hugewarning(f'Killing module: "{module}"')
scan.kill_module(module, message="killed by user")
else:
log.warning(f'Invalid module: "{module}"')
else:
scan.preset.core.logger.toggle_log_level(logger=log)
scan.modules_status(_log=True)
reader = asyncio.StreamReader()
protocol = asyncio.StreamReaderProtocol(reader)
await asyncio.get_running_loop().connect_read_pipe(lambda: protocol, sys.stdin)
# set stdout and stderr to blocking mode
# this is needed to prevent BlockingIOErrors in logging etc.
fds = []
for stream in [sys.stdout, sys.stderr]:
try:
fds.append(stream.fileno())
except io.UnsupportedOperation:
log.debug(f"Can't get fileno for {stream}")
for fd in fds:
flags = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, flags & ~os.O_NONBLOCK)
async def akeyboard_listen():
try:
allowed_errors = 10
while 1:
keyboard_input = None
try:
keyboard_input = smart_decode((await reader.readline()).strip())
allowed_errors = 10
except Exception as e:
log_to_stderr(f"Error in keyboard listen loop: {e}", level="TRACE")
log_to_stderr(traceback.format_exc(), level="TRACE")
allowed_errors -= 1
if keyboard_input is not None:
handle_keyboard_input(keyboard_input)
if allowed_errors <= 0:
break
except Exception as e:
log_to_stderr(f"Error in keyboard listen task: {e}", level="ERROR")
log_to_stderr(traceback.format_exc(), level="TRACE")
keyboard_listen_task = asyncio.create_task(akeyboard_listen()) # noqa F841
await scan.async_start_without_generator()
return True
except BBOTError as e:
log.error(str(e))
log.trace(traceback.format_exc())
finally:
# save word cloud
with suppress(BaseException):
scan.helpers.word_cloud.save()
# remove output directory if empty
with suppress(BaseException):
scan.home.rmdir()
def main():
import asyncio
import traceback
from bbot.core import CORE
global scan_name
try:
asyncio.run(_main())
except asyncio.CancelledError:
if CORE.logger.log_level <= logging.DEBUG:
log_to_stderr(traceback.format_exc(), level="DEBUG")
except KeyboardInterrupt:
msg = "Interrupted"
if scan_name:
msg = f"You killed {scan_name}"
log_to_stderr(msg, level="WARNING")
if CORE.logger.log_level <= logging.DEBUG:
log_to_stderr(traceback.format_exc(), level="DEBUG")
exit(1)
if __name__ == "__main__":
main()
================================================
FILE: bbot/core/__init__.py
================================================
from .core import BBOTCore
CORE = BBOTCore()
================================================
FILE: bbot/core/config/__init__.py
================================================
import sys
import multiprocessing as mp
try:
mp.set_start_method("spawn")
except Exception:
start_method = mp.get_start_method()
if start_method != "spawn":
print(
f"[WARN] Multiprocessing spawn method is set to {start_method}. This may negatively affect performance.",
file=sys.stderr,
)
================================================
FILE: bbot/core/config/files.py
================================================
import sys
from pathlib import Path
from omegaconf import OmegaConf
from ...logger import log_to_stderr
from ...errors import ConfigLoadError
bbot_code_dir = Path(__file__).parent.parent.parent
class BBOTConfigFiles:
config_dir = (Path.home() / ".config" / "bbot").resolve()
defaults_filename = (bbot_code_dir / "defaults.yml").resolve()
config_filename = (config_dir / "bbot.yml").resolve()
secrets_filename = (config_dir / "secrets.yml").resolve()
def __init__(self, core):
self.core = core
def _get_config(self, filename, name="config"):
filename = Path(filename).resolve()
try:
conf = OmegaConf.load(str(filename))
cli_silent = any(x in sys.argv for x in ("-s", "--silent"))
if __name__ == "__main__" and not cli_silent:
log_to_stderr(f"Loaded {name} from {filename}")
return conf
except Exception as e:
if filename.exists():
raise ConfigLoadError(f"Error parsing config at {filename}:\n\n{e}")
return OmegaConf.create()
def get_custom_config(self):
return OmegaConf.merge(
self._get_config(self.config_filename, name="config"),
self._get_config(self.secrets_filename, name="secrets"),
)
def get_default_config(self):
return self._get_config(self.defaults_filename, name="defaults")
================================================
FILE: bbot/core/config/logger.py
================================================
import os
import sys
import atexit
import logging
from copy import copy
import multiprocessing
import logging.handlers
from pathlib import Path
from contextlib import suppress
from ..helpers.misc import mkdir, error_and_exit
from ..multiprocess import SHARED_INTERPRETER_STATE
from ...logger import colorize, loglevel_mapping, GzipRotatingFileHandler
debug_format = logging.Formatter("%(asctime)s [%(levelname)s] %(name)s %(filename)s:%(lineno)s %(message)s")
class ColoredFormatter(logging.Formatter):
"""
Pretty colors for terminal
"""
formatter = logging.Formatter("%(levelname)s %(message)s")
module_formatter = logging.Formatter("%(levelname)s %(name)s: %(message)s")
def format(self, record):
colored_record = copy(record)
levelname = colored_record.levelname
levelshort = loglevel_mapping.get(levelname, "INFO")
colored_record.levelname = colorize(f"[{levelshort}]", level=levelname)
if levelname == "CRITICAL" or levelname.startswith("HUGE"):
colored_record.msg = colorize(colored_record.msg, level=levelname)
# remove name
if colored_record.name.startswith("bbot.modules."):
colored_record.name = colored_record.name.split("bbot.modules.")[-1]
return self.module_formatter.format(colored_record)
return self.formatter.format(colored_record)
class BBOTLogger:
"""
The main BBOT logger.
The job of this class is to manage the different log handlers in BBOT,
allow adding new log handlers, and easily switching log levels on the fly.
"""
def __init__(self, core):
# custom logging levels
if getattr(logging, "HUGEWARNING", None) is None:
self.addLoggingLevel("TRACE", 49)
self.addLoggingLevel("HUGEWARNING", 31)
self.addLoggingLevel("HUGESUCCESS", 26)
self.addLoggingLevel("SUCCESS", 25)
self.addLoggingLevel("HUGEINFO", 21)
self.addLoggingLevel("HUGEVERBOSE", 16)
self.addLoggingLevel("VERBOSE", 15)
self.verbosity_levels_toggle = [logging.INFO, logging.VERBOSE, logging.DEBUG]
self._loggers = None
self._log_handlers = None
self._log_level = None
self.core_logger = logging.getLogger("bbot")
self.core = core
self.listener = None
# if we haven't set up logging yet, do it now
if "_BBOT_LOGGING_SETUP" not in os.environ:
os.environ["_BBOT_LOGGING_SETUP"] = "1"
self.queue = multiprocessing.Queue()
self.setup_queue_handler()
# Start the QueueListener
self.listener = logging.handlers.QueueListener(self.queue, *self.log_handlers.values())
self.listener.start()
atexit.register(self.cleanup_logging)
self.log_level = logging.INFO
def cleanup_logging(self):
# Close the queue handler
with suppress(Exception):
self.queue_handler.close()
# Clean all other loggers
for logger in logging.Logger.manager.loggerDict.values():
if hasattr(logger, "handlers"): # Logger, not PlaceHolder
for handler in list(logger.handlers):
with suppress(Exception):
logger.removeHandler(handler)
with suppress(Exception):
handler.close()
# Stop queue listener
with suppress(Exception):
self.listener.stop()
def setup_queue_handler(self, logging_queue=None, log_level=logging.DEBUG):
if logging_queue is None:
logging_queue = self.queue
else:
self.queue = logging_queue
self.queue_handler = logging.handlers.QueueHandler(logging_queue)
self.core_logger.addHandler(self.queue_handler)
self.core_logger.setLevel(log_level)
# disable asyncio logging for child processes
if not SHARED_INTERPRETER_STATE.is_main_process:
logging.getLogger("asyncio").setLevel(logging.ERROR)
def addLoggingLevel(self, levelName, levelNum, methodName=None):
"""
Comprehensively adds a new logging level to the `logging` module and the
currently configured logging class.
`levelName` becomes an attribute of the `logging` module with the value
`levelNum`. `methodName` becomes a convenience method for both `logging`
itself and the class returned by `logging.getLoggerClass()` (usually just
`logging.Logger`). If `methodName` is not specified, `levelName.lower()` is
used.
To avoid accidental clobberings of existing attributes, this method will
raise an `AttributeError` if the level name is already an attribute of the
`logging` module or if the method name is already present
Example
-------
>>> addLoggingLevel('TRACE', logging.DEBUG - 5)
>>> logging.getLogger(__name__).setLevel('TRACE')
>>> logging.getLogger(__name__).trace('that worked')
>>> logging.trace('so did this')
>>> logging.TRACE
5
"""
if not methodName:
methodName = levelName.lower()
if hasattr(logging, levelName):
raise AttributeError(f"{levelName} already defined in logging module")
if hasattr(logging, methodName):
raise AttributeError(f"{methodName} already defined in logging module")
if hasattr(logging.getLoggerClass(), methodName):
raise AttributeError(f"{methodName} already defined in logger class")
# This method was inspired by the answers to Stack Overflow post
# http://stackoverflow.com/q/2183233/2988730, especially
# http://stackoverflow.com/a/13638084/2988730
def logForLevel(self, message, *args, **kwargs):
if self.isEnabledFor(levelNum):
self._log(levelNum, message, args, **kwargs)
def logToRoot(message, *args, **kwargs):
logging.log(levelNum, message, *args, **kwargs)
logging.addLevelName(levelNum, levelName)
setattr(logging, levelName, levelNum)
setattr(logging.getLoggerClass(), methodName, logForLevel)
setattr(logging, methodName, logToRoot)
@property
def loggers(self):
if self._loggers is None:
self._loggers = [
logging.getLogger("bbot"),
logging.getLogger("asyncio"),
]
return self._loggers
def add_log_handler(self, handler, formatter=None):
if self.listener is None:
return
if handler.formatter is None:
handler.setFormatter(debug_format)
if handler not in self.listener.handlers:
self.listener.handlers = self.listener.handlers + (handler,)
def remove_log_handler(self, handler):
if self.listener is None:
return
if handler in self.listener.handlers:
new_handlers = list(self.listener.handlers)
new_handlers.remove(handler)
self.listener.handlers = tuple(new_handlers)
def include_logger(self, logger):
if logger not in self.loggers:
self.loggers.append(logger)
if self.log_level is not None:
logger.setLevel(self.log_level)
for handler in self.log_handlers.values():
self.add_log_handler(handler)
def stderr_filter(self, record):
if record.levelno == logging.TRACE and self.log_level > logging.DEBUG:
return False
if record.levelno < self.log_level:
return False
return True
@property
def log_handlers(self):
if self._log_handlers is None:
log_dir = Path(self.core.home) / "logs"
if not mkdir(log_dir, raise_error=False):
error_and_exit(f"Failure creating or error writing to BBOT logs directory ({log_dir})")
# Main log file
main_handler = GzipRotatingFileHandler(f"{log_dir}/bbot.log", maxBytes=1024 * 1024 * 100, backupCount=100)
# Separate log file for debugging
debug_handler = GzipRotatingFileHandler(
f"{log_dir}/bbot.debug.log", maxBytes=1024 * 1024 * 100, backupCount=100
)
# Log to stderr
stderr_handler = logging.StreamHandler(sys.stderr)
stderr_handler.addFilter(self.stderr_filter)
# log to files
debug_handler.addFilter(lambda x: x.levelno == logging.TRACE or (x.levelno < logging.VERBOSE))
main_handler.addFilter(lambda x: x.levelno != logging.TRACE and x.levelno >= logging.VERBOSE)
# Set log format
debug_handler.setFormatter(debug_format)
main_handler.setFormatter(debug_format)
stderr_handler.setFormatter(ColoredFormatter("%(levelname)s %(name)s: %(message)s"))
self._log_handlers = {
"stderr": stderr_handler,
"file_debug": debug_handler,
"file_main": main_handler,
}
return self._log_handlers
@property
def log_level(self):
if self._log_level is None:
return logging.INFO
return self._log_level
@log_level.setter
def log_level(self, level):
self.set_log_level(level)
def set_log_level(self, level, logger=None):
if isinstance(level, str):
level = logging.getLevelName(level)
if logger is not None:
logger.hugeinfo(f"Setting log level to {logging.getLevelName(level)}")
self._log_level = level
for logger in self.loggers:
logger.setLevel(level)
def toggle_log_level(self, logger=None):
if self.log_level in self.verbosity_levels_toggle:
for i, level in enumerate(self.verbosity_levels_toggle):
if self.log_level == level:
self.set_log_level(
self.verbosity_levels_toggle[(i + 1) % len(self.verbosity_levels_toggle)], logger=logger
)
break
else:
self.set_log_level(self.verbosity_levels_toggle[0], logger=logger)
================================================
FILE: bbot/core/core.py
================================================
import os
import logging
from copy import copy
from pathlib import Path
from contextlib import suppress
from omegaconf import OmegaConf
from bbot.errors import BBOTError
from .multiprocess import SHARED_INTERPRETER_STATE
DEFAULT_CONFIG = None
class BBOTCore:
"""
This is the first thing that loads when you import BBOT.
Unlike a Preset, BBOTCore holds only the config, not scan-specific stuff like targets, flags, modules, etc.
Its main jobs are:
- set up logging
- keep separation between the `default` and `custom` config (this allows presets to only display the config options that have changed)
- allow for easy merging of configs
- load quickly
"""
# used for filtering out sensitive config values
secrets_strings = ["api_key", "username", "password", "token", "secret", "_id"]
# don't filter/remove entries under this key
secrets_exclude_keys = ["modules"]
def __init__(self):
self._logger = None
self._files_config = None
self._config = None
self._custom_config = None
# bare minimum == logging
self.logger
self.log = logging.getLogger("bbot.core")
self._prep_multiprocessing()
def _prep_multiprocessing(self):
import multiprocessing
from .helpers.process import BBOTProcess
if SHARED_INTERPRETER_STATE.is_main_process:
# if this is the main bbot process, set the logger and queue for the first time
from functools import partialmethod
BBOTProcess.__init__ = partialmethod(
BBOTProcess.__init__, log_level=self.logger.log_level, log_queue=self.logger.queue
)
# this makes our process class the default for process pools, etc.
mp_context = multiprocessing.get_context("spawn")
mp_context.Process = BBOTProcess
@property
def home(self):
return Path(self.config["home"]).expanduser().resolve()
@property
def cache_dir(self):
return self.home / "cache"
@property
def tools_dir(self):
return self.home / "tools"
@property
def temp_dir(self):
return self.home / "temp"
@property
def lib_dir(self):
return self.home / "lib"
@property
def scans_dir(self):
return self.home / "scans"
@property
def config(self):
"""
.config is just .default_config + .custom_config merged together
any new values should be added to custom_config.
"""
if self._config is None:
self._config = OmegaConf.merge(self.default_config, self.custom_config)
# set read-only flag (change .custom_config instead)
OmegaConf.set_readonly(self._config, True)
return self._config
@property
def default_config(self):
"""
The default BBOT config (from `defaults.yml`). Read-only.
"""
global DEFAULT_CONFIG
if DEFAULT_CONFIG is None:
self.default_config = self.files_config.get_default_config()
# ensure bbot home dir
if "home" not in self.default_config:
self.default_config["home"] = "~/.bbot"
return DEFAULT_CONFIG
@default_config.setter
def default_config(self, value):
# we temporarily clear out the config so it can be refreshed if/when default_config changes
global DEFAULT_CONFIG
self._config = None
DEFAULT_CONFIG = value
# set read-only flag (change .custom_config instead)
OmegaConf.set_readonly(DEFAULT_CONFIG, True)
@property
def custom_config(self):
"""
Custom BBOT config (from `~/.config/bbot/bbot.yml`)
"""
# we temporarily clear out the config so it can be refreshed if/when custom_config changes
self._config = None
if self._custom_config is None:
self.custom_config = self.files_config.get_custom_config()
return self._custom_config
@custom_config.setter
def custom_config(self, value):
# we temporarily clear out the config so it can be refreshed if/when custom_config changes
self._config = None
# ensure the modules key is always a dictionary
modules_entry = value.get("modules", None)
if modules_entry is not None and not OmegaConf.is_dict(modules_entry):
value["modules"] = {}
self._custom_config = value
def no_secrets_config(self, config):
from .helpers.misc import clean_dict
with suppress(ValueError):
config = OmegaConf.to_object(config)
return clean_dict(
config,
*self.secrets_strings,
fuzzy=True,
exclude_keys=self.secrets_exclude_keys,
)
def secrets_only_config(self, config):
from .helpers.misc import filter_dict
with suppress(ValueError):
config = OmegaConf.to_object(config)
return filter_dict(
config,
*self.secrets_strings,
fuzzy=True,
exclude_keys=self.secrets_exclude_keys,
)
def merge_custom(self, config):
"""
Merge a config into the custom config.
"""
self.custom_config = OmegaConf.merge(self.custom_config, OmegaConf.create(config))
def merge_default(self, config):
"""
Merge a config into the default config.
"""
self.default_config = OmegaConf.merge(self.default_config, OmegaConf.create(config))
def copy(self):
"""
Return a semi-shallow copy of self. (`custom_config` is copied, but `default_config` stays the same)
"""
core_copy = copy(self)
core_copy._custom_config = self._custom_config.copy()
return core_copy
@property
def files_config(self):
"""
Get the configs from `bbot.yml` and `defaults.yml`
"""
if self._files_config is None:
from .config import files
self.files = files
self._files_config = files.BBOTConfigFiles(self)
return self._files_config
def create_process(self, *args, **kwargs):
if os.environ.get("BBOT_TESTING", "") == "True":
process = self.create_thread(*args, **kwargs)
else:
if SHARED_INTERPRETER_STATE.is_scan_process:
from .helpers.process import BBOTProcess
process = BBOTProcess(*args, **kwargs)
else:
import multiprocessing
raise BBOTError(f"Tried to start server from process {multiprocessing.current_process().name}")
process.daemon = True
return process
def create_thread(self, *args, **kwargs):
from .helpers.process import BBOTThread
return BBOTThread(*args, **kwargs)
@property
def logger(self):
self.config
if self._logger is None:
from .config.logger import BBOTLogger
self._logger = BBOTLogger(self)
return self._logger
================================================
FILE: bbot/core/engine.py
================================================
import os
import sys
import zmq
import pickle
import asyncio
import inspect
import logging
import tempfile
import traceback
import contextlib
import contextvars
import zmq.asyncio
import multiprocessing
from pathlib import Path
from concurrent.futures import CancelledError
from contextlib import asynccontextmanager, suppress
from bbot.core import CORE
from bbot.errors import BBOTEngineError
from bbot.core.helpers.async_helpers import get_event_loop
from bbot.core.multiprocess import SHARED_INTERPRETER_STATE
from bbot.core.helpers.misc import rand_string, in_exception_chain
error_sentinel = object()
class EngineBase:
"""
Base Engine class for Server and Client.
An Engine is a simple and lightweight RPC implementation that allows offloading async tasks
to a separate process. It leverages ZeroMQ in a ROUTER-DEALER configuration.
BBOT makes use of this by spawning a dedicated engine for DNS and HTTP tasks.
This offloads I/O and helps free up the main event loop for other tasks.
To use Engine, you must subclass both EngineClient and EngineServer.
See the respective EngineClient and EngineServer classes for usage examples.
"""
ERROR_CLASS = BBOTEngineError
def __init__(self, debug=False):
self._shutdown_status = False
self.log = logging.getLogger(f"bbot.core.{self.__class__.__name__.lower()}")
self._engine_debug = debug
def pickle(self, obj):
try:
return pickle.dumps(obj)
except Exception as e:
self.log.error(f"Error serializing object: {obj}: {e}")
self.log.trace(traceback.format_exc())
return error_sentinel
def unpickle(self, binary):
try:
return pickle.loads(binary)
except Exception as e:
self.log.error(f"Error deserializing binary: {e}")
self.log.trace(f"Offending binary: {binary}")
self.log.trace(traceback.format_exc())
return error_sentinel
async def _infinite_retry(self, callback, *args, **kwargs):
interval = kwargs.pop("_interval", 300)
context = kwargs.pop("_context", "")
# default overall timeout of 10 minutes (300 second interval * 2 iterations)
max_retries = kwargs.pop("_max_retries", 1)
if not context:
context = f"{callback.__name__}({args}, {kwargs})"
retries = 0
while not self._shutdown_status:
try:
return await asyncio.wait_for(callback(*args, **kwargs), timeout=interval)
except (TimeoutError, asyncio.exceptions.TimeoutError):
self.log.debug(f"{self.name}: Timeout after {interval:,} seconds {context}, retrying...")
retries += 1
if max_retries is not None and retries > max_retries:
raise TimeoutError(f"Timed out after {(max_retries + 1) * interval:,} seconds {context}")
def engine_debug(self, *args, **kwargs):
if self._engine_debug:
self.log.trace(*args, **kwargs)
class EngineClient(EngineBase):
"""
The client portion of BBOT's RPC Engine.
To create an engine, you must create a subclass of this class and also
define methods for each of your desired functions.
Note that this only supports async functions. If you need to offload a synchronous function to another CPU, use BBOT's multiprocessing pool instead.
Any CPU or I/O intense logic should be implemented in the EngineServer.
These functions are typically stubs whose only job is to forward the arguments to the server.
Functions with the same names should be defined on the EngineServer.
The EngineClient must specify its associated server class via the `SERVER_CLASS` variable.
Depending on whether your function is a generator, you will use either `run_and_return()`, or `run_and_yield`.
Examples:
>>> from bbot.core.engine import EngineClient
>>>
>>> class MyClient(EngineClient):
>>> SERVER_CLASS = MyServer
>>>
>>> async def my_function(self, **kwargs)
>>> return await self.run_and_return("my_function", **kwargs)
>>>
>>> async def my_generator(self, **kwargs):
>>> async for _ in self.run_and_yield("my_generator", **kwargs):
>>> yield _
"""
SERVER_CLASS = None
def __init__(self, debug=False, **kwargs):
self.name = f"EngineClient {self.__class__.__name__}"
super().__init__(debug=debug)
self.process = None
if self.SERVER_CLASS is None:
raise ValueError(f"Must set EngineClient SERVER_CLASS, {self.SERVER_CLASS}")
self.CMDS = dict(self.SERVER_CLASS.CMDS)
for k, v in list(self.CMDS.items()):
self.CMDS[v] = k
self.socket_address = f"zmq_{rand_string(8)}.sock"
self.socket_path = Path(tempfile.gettempdir()) / self.socket_address
self.server_kwargs = kwargs.pop("server_kwargs", {})
self._server_process = None
self.context = zmq.asyncio.Context()
self.context.setsockopt(zmq.LINGER, 0)
self.sockets = set()
def check_error(self, message):
if isinstance(message, dict) and len(message) == 1 and "_e" in message:
self.engine_debug(f"{self.name}: got error message: {message}")
error, trace = message["_e"]
error = self.ERROR_CLASS(error)
error.engine_traceback = trace
self.engine_debug(f"{self.name}: raising {error.__class__.__name__}")
raise error
return False
async def run_and_return(self, command, *args, **kwargs):
fn_str = f"{command}({args}, {kwargs})"
self.engine_debug(f"{self.name}: executing run-and-return {fn_str}")
if self._shutdown_status and not command == "_shutdown":
self.log.verbose(f"{self.name} has been shut down and is not accepting new tasks")
return
async with self.new_socket() as socket:
try:
message = self.make_message(command, args=args, kwargs=kwargs)
if message is error_sentinel:
return
await socket.send(message)
binary = await self._infinite_retry(socket.recv, _context=f"waiting for return value from {fn_str}")
except BaseException:
try:
await self.send_cancel_message(socket, fn_str)
except Exception:
self.log.debug(f"{self.name}: {fn_str} failed to send cancel message after exception")
self.log.trace(traceback.format_exc())
raise
# self.log.debug(f"{self.name}.{command}({kwargs}) got binary: {binary}")
message = self.unpickle(binary)
self.engine_debug(f"{self.name}: {fn_str} got return value: {message}")
# error handling
if self.check_error(message):
return
return message
async def run_and_yield(self, command, *args, **kwargs):
fn_str = f"{command}({args}, {kwargs})"
self.engine_debug(f"{self.name}: executing run-and-yield {fn_str}")
if self._shutdown_status:
self.log.verbose("Engine has been shut down and is not accepting new tasks")
return
message = self.make_message(command, args=args, kwargs=kwargs)
if message is error_sentinel:
return
async with self.new_socket() as socket:
# TODO: synchronize server-side generator by limiting qsize
# socket.setsockopt(zmq.RCVHWM, 1)
# socket.setsockopt(zmq.SNDHWM, 1)
await socket.send(message)
while 1:
try:
binary = await self._infinite_retry(
socket.recv, _context=f"waiting for new iteration from {fn_str}"
)
# self.log.debug(f"{self.name}.{command}({kwargs}) got binary: {binary}")
message = self.unpickle(binary)
self.engine_debug(f"{self.name}: {fn_str} got iteration: {message}")
# error handling
if self.check_error(message) or self.check_stop(message):
break
yield message
except (StopAsyncIteration, GeneratorExit) as e:
exc_name = e.__class__.__name__
self.engine_debug(f"{self.name}.{command} got {exc_name}")
try:
await self.send_cancel_message(socket, fn_str)
except Exception:
self.engine_debug(f"{self.name}.{command} failed to send cancel message after {exc_name}")
self.log.trace(traceback.format_exc())
break
async def send_cancel_message(self, socket, context):
"""
Send a cancel message and wait for confirmation from the server
"""
# -1 == special "cancel" signal
message = pickle.dumps({"c": -1})
await self._infinite_retry(socket.send, message)
while 1:
response = await self._infinite_retry(
socket.recv, _context=f"waiting for CANCEL_OK from {context}", _max_retries=4
)
response = pickle.loads(response)
if isinstance(response, dict):
response = response.get("m", "")
if response == "CANCEL_OK":
break
async def send_shutdown_message(self):
async with self.new_socket() as socket:
# -99 == special shutdown message
message = pickle.dumps({"c": -99})
with suppress(TimeoutError, asyncio.exceptions.TimeoutError):
await asyncio.wait_for(socket.send(message), 0.5)
with suppress(TimeoutError, asyncio.exceptions.TimeoutError):
while 1:
response = await asyncio.wait_for(socket.recv(), 0.5)
response = pickle.loads(response)
if isinstance(response, dict):
response = response.get("m", "")
if response == "SHUTDOWN_OK":
break
def check_stop(self, message):
if isinstance(message, dict) and len(message) == 1 and "_s" in message:
return True
return False
def make_message(self, command, args=None, kwargs=None):
try:
cmd_id = self.CMDS[command]
except KeyError:
raise KeyError(f'Command "{command}" not found. Available commands: {",".join(self.available_commands)}')
message = {"c": cmd_id}
if args:
message["a"] = args
if kwargs:
message["k"] = kwargs
return pickle.dumps(message)
@property
def available_commands(self):
return [s for s in self.CMDS if isinstance(s, str)]
def start_server(self):
process_name = multiprocessing.current_process().name
if SHARED_INTERPRETER_STATE.is_scan_process:
kwargs = dict(self.server_kwargs)
# if we're in tests, we use a single event loop to avoid weird race conditions
# this allows us to more easily mock http, etc.
if os.environ.get("BBOT_TESTING", "") == "True":
kwargs["_loop"] = get_event_loop()
kwargs["debug"] = self._engine_debug
self.process = CORE.create_process(
target=self.server_process,
args=(
self.SERVER_CLASS,
self.socket_path,
),
kwargs=kwargs,
custom_name=f"BBOT {self.__class__.__name__}",
)
self.process.start()
return self.process
else:
raise BBOTEngineError(
f"Tried to start server from process {process_name}. Did you forget \"if __name__ == '__main__'?\""
)
@staticmethod
def server_process(server_class, socket_path, **kwargs):
try:
loop = kwargs.pop("_loop", None)
engine_server = server_class(socket_path, **kwargs)
if loop is not None:
future = asyncio.run_coroutine_threadsafe(engine_server.worker(), loop)
future.result()
else:
asyncio.run(engine_server.worker())
except (asyncio.CancelledError, KeyboardInterrupt, CancelledError):
return
except Exception:
import traceback
log = logging.getLogger("bbot.core.engine.server")
log.critical(f"Unhandled error in {server_class.__name__} server process: {traceback.format_exc()}")
@asynccontextmanager
async def new_socket(self):
if self._server_process is None:
self._server_process = self.start_server()
while not self.socket_path.exists():
self.engine_debug(f"{self.name}: waiting for server process to start...")
await asyncio.sleep(0.1)
socket = self.context.socket(zmq.DEALER)
socket.setsockopt(zmq.LINGER, 0) # Discard pending messages immediately disconnect() or close()
socket.setsockopt(zmq.SNDHWM, 0) # Unlimited send buffer
socket.setsockopt(zmq.RCVHWM, 0) # Unlimited receive buffer
socket.connect(f"ipc://{self.socket_path}")
self.sockets.add(socket)
try:
yield socket
finally:
self.sockets.remove(socket)
with suppress(Exception):
socket.close()
async def shutdown(self):
if not self._shutdown_status:
self._shutdown_status = True
self.log.verbose(f"{self.name}: shutting down...")
# send shutdown signal
await self.send_shutdown_message()
# then terminate context
try:
self.context.destroy(linger=0)
except Exception:
print(traceback.format_exc(), file=sys.stderr)
try:
self.context.term()
except Exception:
print(traceback.format_exc(), file=sys.stderr)
# delete socket file on exit
self.socket_path.unlink(missing_ok=True)
class EngineServer(EngineBase):
"""
The server portion of BBOT's RPC Engine.
Methods defined here must match the methods in your EngineClient.
To use the functions, you must create mappings for them in the CMDS attribute, as shown below.
Examples:
>>> from bbot.core.engine import EngineServer
>>>
>>> class MyServer(EngineServer):
>>> CMDS = {
>>> 0: "my_function",
>>> 1: "my_generator",
>>> }
>>>
>>> def my_function(self, arg1=None):
>>> await asyncio.sleep(1)
>>> return str(arg1)
>>>
>>> def my_generator(self):
>>> for i in range(10):
>>> await asyncio.sleep(1)
>>> yield i
"""
CMDS = {}
def __init__(self, socket_path, debug=False):
self.name = f"EngineServer {self.__class__.__name__}"
super().__init__(debug=debug)
self.engine_debug(f"{self.name}: finished setup 1 (_debug={self._engine_debug})")
self.socket_path = socket_path
self.client_id_var = contextvars.ContextVar("client_id", default=None)
# task <--> client id mapping
self.tasks = {}
# child tasks spawned by main tasks
self.child_tasks = {}
self.engine_debug(f"{self.name}: finished setup 2 (_debug={self._engine_debug})")
if self.socket_path is not None:
# create ZeroMQ context
self.context = zmq.asyncio.Context()
# ROUTER socket can handle multiple concurrent requests
self.socket = self.context.socket(zmq.ROUTER)
self.socket.setsockopt(zmq.LINGER, 0) # Discard pending messages immediately disconnect() or close()
self.socket.setsockopt(zmq.SNDHWM, 0) # Unlimited send buffer
self.socket.setsockopt(zmq.RCVHWM, 0) # Unlimited receive buffer
# create socket file
self.socket.bind(f"ipc://{self.socket_path}")
self.engine_debug(f"{self.name}: finished setup 3 (_debug={self._engine_debug})")
@contextlib.contextmanager
def client_id_context(self, value):
token = self.client_id_var.set(value)
try:
yield
finally:
self.client_id_var.reset(token)
async def run_and_return(self, client_id, command_fn, *args, **kwargs):
fn_str = f"{command_fn.__name__}({args}, {kwargs})"
self.engine_debug(fn_str)
with self.client_id_context(client_id):
try:
self.engine_debug(f"{self.name}: starting run-and-return {fn_str}")
try:
result = await command_fn(*args, **kwargs)
except BaseException as e:
if in_exception_chain(e, (KeyboardInterrupt, asyncio.CancelledError)):
log_fn = self.log.debug
else:
log_fn = self.log.error
error = f"{self.name}: error in {fn_str}: {e}"
trace = traceback.format_exc()
log_fn(error)
self.log.trace(trace)
result = {"_e": (error, trace)}
finally:
self.tasks.pop(client_id, None)
self.engine_debug(f"{self.name}: sending response to {fn_str}: {result}")
await self.send_socket_multipart(client_id, result)
except BaseException as e:
self.log.critical(
f"Unhandled exception in {self.name}.run_and_return({client_id}, {command_fn}, {args}, {kwargs}): {e}"
)
self.log.critical(traceback.format_exc())
finally:
self.engine_debug(f"{self.name} finished run-and-return {fn_str}")
async def run_and_yield(self, client_id, command_fn, *args, **kwargs):
fn_str = f"{command_fn.__name__}({args}, {kwargs})"
with self.client_id_context(client_id):
try:
self.engine_debug(f"{self.name}: starting run-and-yield {fn_str}")
try:
async for _ in command_fn(*args, **kwargs):
self.engine_debug(f"{self.name}: sending iteration for {fn_str}: {_}")
await self.send_socket_multipart(client_id, _)
except BaseException as e:
if in_exception_chain(e, (KeyboardInterrupt, asyncio.CancelledError)):
log_fn = self.log.debug
else:
log_fn = self.log.error
error = f"{self.name}: error in {fn_str}: {e}"
trace = traceback.format_exc()
log_fn(error)
self.log.trace(trace)
result = {"_e": (error, trace)}
await self.send_socket_multipart(client_id, result)
finally:
self.engine_debug(f"{self.name}: reached end of run-and-yield iteration for {fn_str}")
# _s == special signal that means StopIteration
await self.send_socket_multipart(client_id, {"_s": None})
self.tasks.pop(client_id, None)
except BaseException as e:
self.log.critical(
f"Unhandled exception in {self.name}.run_and_yield({client_id}, {command_fn}, {args}, {kwargs}): {e}"
)
self.log.critical(traceback.format_exc())
finally:
self.engine_debug(f"{self.name}: finished run-and-yield {fn_str}")
async def send_socket_multipart(self, client_id, message):
try:
message = pickle.dumps(message)
await self._infinite_retry(self.socket.send_multipart, [client_id, message])
except Exception as e:
self.log.verbose(f"{self.name}: error sending ZMQ message: {e}")
self.log.trace(traceback.format_exc())
def check_error(self, message):
if message is error_sentinel:
return True
async def worker(self):
self.engine_debug(f"{self.name}: starting worker")
try:
while 1:
client_id, binary = await self.socket.recv_multipart()
message = self.unpickle(binary)
self.engine_debug(f"{self.name} got message: {message}")
if self.check_error(message):
continue
cmd = message.get("c", None)
if not isinstance(cmd, int):
self.log.warning(f"{self.name}: no command sent in message: {message}")
continue
# -1 == cancel task
if cmd == -1:
self.engine_debug(f"{self.name} got cancel signal")
await self.send_socket_multipart(client_id, {"m": "CANCEL_OK"})
await self.cancel_task(client_id)
continue
# -99 == shutdown task
if cmd == -99:
self.log.verbose(f"{self.name} got shutdown signal")
await self.send_socket_multipart(client_id, {"m": "SHUTDOWN_OK"})
await self._shutdown()
return
args = message.get("a", ())
if not isinstance(args, tuple):
self.log.warning(f"{self.name}: received invalid args of type {type(args)}, should be tuple")
continue
kwargs = message.get("k", {})
if not isinstance(kwargs, dict):
self.log.warning(f"{self.name}: received invalid kwargs of type {type(kwargs)}, should be dict")
continue
command_name = self.CMDS[cmd]
command_fn = getattr(self, command_name, None)
if command_fn is None:
self.log.warning(f'{self.name} has no function named "{command_fn}"')
continue
if inspect.isasyncgenfunction(command_fn):
self.engine_debug(f"{self.name}: creating run-and-yield coroutine for {command_name}()")
coroutine = self.run_and_yield(client_id, command_fn, *args, **kwargs)
else:
self.engine_debug(f"{self.name}: creating run-and-return coroutine for {command_name}()")
coroutine = self.run_and_return(client_id, command_fn, *args, **kwargs)
self.engine_debug(f"{self.name}: creating task for {command_name}() coroutine")
task = asyncio.create_task(coroutine)
self.tasks[client_id] = task, command_fn, args, kwargs
self.engine_debug(f"{self.name}: finished creating task for {command_name}() coroutine")
except BaseException as e:
await self._shutdown()
if not in_exception_chain(e, (KeyboardInterrupt, asyncio.CancelledError)):
self.log.error(f"{self.name}: error in EngineServer worker: {e}")
self.log.trace(traceback.format_exc())
finally:
self.engine_debug(f"{self.name}: finished worker()")
async def _shutdown(self):
if not self._shutdown_status:
self.log.verbose(f"{self.name}: shutting down...")
self._shutdown_status = True
await self.cancel_all_tasks()
context = getattr(self, "context", None)
if context is not None:
try:
context.destroy(linger=0)
except Exception:
self.log.trace(traceback.format_exc())
try:
context.term()
except Exception:
self.log.trace(traceback.format_exc())
self.log.verbose(f"{self.name}: finished shutting down")
async def task_pool(self, fn, args_kwargs, threads=10, timeout=300, global_kwargs=None):
if global_kwargs is None:
global_kwargs = {}
tasks = {}
args_kwargs = list(args_kwargs)
def new_task():
if args_kwargs:
kwargs = {}
tracker = None
args = args_kwargs.pop(0)
if isinstance(args, (list, tuple)):
# you can specify a custom tracker value if you want
# this helps with correlating results
with suppress(ValueError):
args, kwargs, tracker = args
# or you can just specify args/kwargs
with suppress(ValueError):
args, kwargs = args
if not isinstance(kwargs, dict):
raise ValueError(f"kwargs must be dict (got: {kwargs})")
if not isinstance(args, (list, tuple)):
args = [args]
task = self.new_child_task(fn(*args, **kwargs, **global_kwargs))
tasks[task] = (args, kwargs, tracker)
for _ in range(threads): # Start initial batch of tasks
new_task()
while tasks: # While there are tasks pending
# Wait for the first task to complete
finished = await self.finished_tasks(tasks, timeout=timeout)
for task in finished:
result = task.result()
(args, kwargs, tracker) = tasks.pop(task)
yield (args, kwargs, tracker), result
new_task()
def new_child_task(self, coro):
"""
Create a new asyncio task, making sure to track it based on the client id.
This allows the task to be automatically cancelled if its parent is cancelled.
"""
client_id = self.client_id_var.get()
task = asyncio.create_task(coro)
if client_id:
def remove_task(t):
tasks = self.child_tasks.get(client_id, set())
tasks.discard(t)
if not tasks:
self.child_tasks.pop(client_id, None)
task.add_done_callback(remove_task)
try:
self.child_tasks[client_id].add(task)
except KeyError:
self.child_tasks[client_id] = {task}
return task
async def finished_tasks(self, tasks, timeout=None):
"""
Given a list of asyncio tasks, return the ones that are finished with an optional timeout
"""
if tasks:
try:
done, _ = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED, timeout=timeout)
return done
except BaseException as e:
if isinstance(e, (TimeoutError, asyncio.exceptions.TimeoutError)):
self.log.warning(f"{self.name}: Timeout after {timeout:,} seconds in finished_tasks({tasks})")
for task in list(tasks):
task.cancel()
self._await_cancelled_task(task)
else:
if not in_exception_chain(e, (KeyboardInterrupt, asyncio.CancelledError)):
self.log.error(f"{self.name}: Unhandled exception in finished_tasks({tasks}): {e}")
self.log.trace(traceback.format_exc())
raise
return set()
async def cancel_task(self, client_id):
parent_task = self.tasks.pop(client_id, None)
if parent_task is None:
return
parent_task, _cmd, _args, _kwargs = parent_task
self.engine_debug(f"{self.name}: Cancelling client id {client_id} (task: {parent_task})")
parent_task.cancel()
child_tasks = self.child_tasks.pop(client_id, set())
if child_tasks:
self.engine_debug(f"{self.name}: Cancelling {len(child_tasks):,} child tasks for client id {client_id}")
for child_task in child_tasks:
child_task.cancel()
for task in [parent_task] + list(child_tasks):
await self._await_cancelled_task(task)
async def _await_cancelled_task(self, task):
try:
await asyncio.wait_for(task, timeout=10)
except (TimeoutError, asyncio.exceptions.TimeoutError):
self.log.trace(f"{self.name}: Timeout cancelling task: {task}")
return
except (KeyboardInterrupt, asyncio.CancelledError):
return
except BaseException as e:
self.log.error(f"Unhandled error in {task.get_coro().__name__}(): {e}")
self.log.trace(traceback.format_exc())
async def cancel_all_tasks(self):
for client_id in list(self.tasks):
await self.cancel_task(client_id)
for client_id, tasks in self.child_tasks.items():
for task in list(tasks):
await self._await_cancelled_task(task)
================================================
FILE: bbot/core/event/__init__.py
================================================
from .base import make_event, update_event, is_event, event_from_json
__all__ = ["make_event", "update_event", "is_event", "event_from_json"]
================================================
FILE: bbot/core/event/base.py
================================================
import io
import re
import uuid
import json
import base64
import logging
import tarfile
import datetime
import ipaddress
import traceback
from pathlib import Path
from typing import Optional
from copy import copy, deepcopy
from contextlib import suppress
from radixtarget import RadixTarget
from pydantic import BaseModel, field_validator
from urllib.parse import urlparse, urljoin, parse_qs
from bbot.errors import *
from .helpers import EventSeed
from bbot.core.helpers import (
extract_words,
is_domain,
is_subdomain,
is_ip,
is_ip_type,
is_ptr,
is_uri,
url_depth,
domain_stem,
make_netloc,
make_ip_type,
recursive_decode,
sha1,
smart_decode,
split_host_port,
tagify,
validators,
get_file_extension,
)
from bbot.core.helpers.web.envelopes import BaseEnvelope
log = logging.getLogger("bbot.core.event")
class BaseEvent:
"""
Represents a piece of data discovered during a BBOT scan.
An Event contains various attributes that provide metadata about the discovered data.
The attributes assist in understanding the context of the Event and facilitate further
filtering and querying. Events are integral in the construction of visual graphs and
are the cornerstone of data exchange between BBOT modules.
You can inherit from this class when creating a new event type. However, it's not always
necessary. You only need to subclass if you want to layer additional functionality on
top of the base class.
Attributes:
type (str): Specifies the type of the event, e.g., `IP_ADDRESS`, `DNS_NAME`.
id (str): An identifier for the event (event type + sha1 hash of data). NOT universally unique.
uuid (UUID): A universally unique identifier for the event.
data (str or dict): The main data for the event, e.g., a URL or IP address.
data_graph (str): Representation of `self.data` for graph nodes (e.g. Neo4j).
data_human (str): Representation of `self.data` for human output.
data_id (str): Representation of `self.data` used to calculate the event's ID (and ultimately its hash, which is used for deduplication)
data_json (str): Representation of `self.data` to be used in JSON serialization.
host (str, IPvXAddress, or IPvXNetwork): The associated IP address or hostname for the event
host_stem (str): An abbreviated representation of hostname that removes the TLD, e.g. "www.evilcorp". Used by the word cloud.
port (int or None): The port associated with the event, if applicable, else None.
words (set): A list of relevant keywords extracted from the event. Used by the word cloud.
scope_distance (int): Indicates how many hops the event is from the main scope; 0 means in-scope.
web_spider_distance (int): The spider distance from the web root, specific to web crawling.
scan (Scanner): The scan object that generated the event.
timestamp (datetime.datetime): The time at which the data was discovered.
resolved_hosts (list of str): List of hosts to which the event data resolves, applicable for URLs and DNS names.
parent (BaseEvent): The parent event that led to the discovery of this event.
parent_id (str): The `id` attribute of the parent event.
parent_uuid (str): The `uuid` attribute of the parent event.
tags (set of str): Descriptive tags for the event, e.g., `mx-record`, `in-scope`.
module (BaseModule): The module that discovered the event.
module_sequence (str): The sequence of modules that participated in the discovery.
Examples:
```json
{
"type": "URL",
"id": "URL:017ec8e5dc158c0fd46f07169f8577fb4b45e89a",
"data": "http://www.blacklanternsecurity.com/",
"web_spider_distance": 0,
"scope_distance": 0,
"scan": "SCAN:4d786912dbc97be199da13074699c318e2067a7f",
"timestamp": 1688526222.723366,
"resolved_hosts": ["185.199.108.153"],
"parent": "OPEN_TCP_PORT:cf7e6a937b161217eaed99f0c566eae045d094c7",
"tags": ["in-scope", "distance-0", "dir", "ip-185-199-108-153", "status-301", "http-title-301-moved-permanently"],
"module": "httpx",
"module_sequence": "httpx"
}
```
"""
# Always emit this event type even if it's not in scope
_always_emit = False
# Always emit events with these tags even if they're not in scope
_always_emit_tags = ["affiliate", "target"]
# Bypass scope checking and dns resolution, distribute immediately to modules
# This is useful for "end-of-line" events like FINDING and VULNERABILITY
_quick_emit = False
# Data validation, if data is a dictionary
_data_validator = None
# Whether to increment scope distance if the child and parent hosts are the same
# Normally we don't want this, since scope distance only increases if the host changes
# But for some events like SOCIAL media profiles, this is required to prevent spidering all of facebook.com
_scope_distance_increment_same_host = False
# Don't allow duplicates to occur within a parent chain
# In other words, don't emit the event if the same one already exists in its discovery context
_suppress_chain_dupes = False
# using __slots__ dramatically reduces memory usage in large scans
__slots__ = [
# Core identification attributes
"_uuid",
"_id",
"_hash",
"_data",
"_data_hash",
# Host-related attributes
"__host",
"_host_original",
"_port",
# Parent-related attributes
"_parent",
"_parent_id",
"_parent_uuid",
# Event metadata
"_type",
"_tags",
"_omit",
"__words",
"_priority",
"_scope_distance",
"_module_priority",
"_graph_important",
"_resolved_hosts",
"_discovery_context",
"_discovery_context_regex",
"_stats_recorded",
"_internal",
"_confidence",
"_dummy",
"_module",
# DNS-related attributes
"dns_children",
"raw_dns_records",
"dns_resolve_distance",
# Web-related attributes
"web_spider_distance",
"parsed_url",
"url_extension",
"num_redirects",
# File-related attributes
"_data_path",
# Public attributes
"module",
"scan",
"timestamp",
]
def __init__(
self,
data,
event_type,
parent=None,
context=None,
module=None,
scan=None,
tags=None,
confidence=100,
timestamp=None,
_dummy=False,
_internal=None,
):
"""
Initializes an Event object with the given parameters.
In most cases, you should use `make_event()` instead of instantiating this class directly.
`make_event()` is much friendlier, and can auto-detect the event type for you.
Attributes:
data (str, dict): The primary data for the event.
event_type (str, optional): Type of the event, e.g., 'IP_ADDRESS'.
parent (BaseEvent, optional): Parent event that led to this event's discovery. Defaults to None.
module (str, optional): Module that discovered the event. Defaults to None.
scan (Scan, optional): BBOT Scan object. Required unless _dummy is True. Defaults to None.
tags (list of str, optional): Descriptive tags for the event. Defaults to None.
confidence (int, optional): Confidence level for the event, on a scale of 1-100. Defaults to 100.
timestamp (datetime, optional): Time of event discovery. Defaults to current UTC time.
_dummy (bool, optional): If True, disables certain data validations. Defaults to False.
_internal (Any, optional): If specified, makes the event internal. Defaults to None.
Raises:
ValidationError: If either `scan` or `parent` are not specified and `_dummy` is False.
"""
self._uuid = uuid.uuid4()
self._id = None
self._hash = None
self._data = None
self.__host = None
self._tags = set()
self._port = None
self._omit = False
self.__words = None
self._parent = None
self._priority = None
self._parent_id = None
self._parent_uuid = None
self._host_original = None
self._scope_distance = None
self._module_priority = None
self._graph_important = False
self._resolved_hosts = set()
self.dns_children = {}
self.raw_dns_records = {}
self._discovery_context = ""
self._discovery_context_regex = re.compile(r"\{(?:event|module)[^}]*\}")
self.web_spider_distance = 0
# for creating one-off events without enforcing parent requirement
self._dummy = _dummy
self.module = module
self._type = event_type
# keep track of whether this event has been recorded by the scan
self._stats_recorded = False
if timestamp is not None:
self.timestamp = timestamp
else:
try:
self.timestamp = datetime.datetime.now(datetime.UTC)
except AttributeError:
self.timestamp = datetime.datetime.utcnow()
self.confidence = int(confidence)
self._internal = False
# self.scan holds the instantiated scan object (for helpers, etc.)
self.scan = scan
if (not self.scan) and (not self._dummy):
raise ValidationError("Must specify scan")
try:
self.data = self._sanitize_data(data)
except Exception as e:
log.trace(traceback.format_exc())
raise ValidationError(f'Error sanitizing event data "{data}" for type "{self.type}": {e}')
if not self.data:
raise ValidationError(f'Invalid event data "{data}" for type "{self.type}"')
self.parent = parent
if (not self.parent) and (not self._dummy):
raise ValidationError("Must specify event parent")
if tags is not None:
for tag in tags:
self.add_tag(tag)
# internal events are not ingested by output modules
if not self._dummy:
# removed this second part because it was making certain sslcert events internal
if _internal: # or parent._internal:
self.internal = True
if not context:
context = getattr(self.module, "default_discovery_context", "")
if context:
self.discovery_context = context
@property
def data(self):
return self._data
@property
def confidence(self):
return self._confidence
@confidence.setter
def confidence(self, confidence):
self._confidence = min(100, max(1, int(confidence)))
@property
def cumulative_confidence(self):
"""
Considers the confidence of parent events. This is useful for filtering out speculative/unreliable events.
E.g. an event with a confidence of 50 whose parent is also 50 would have a cumulative confidence of 25.
A confidence of 100 will reset the cumulative confidence to 100.
"""
if self._confidence == 100 or self.parent is None or self.parent is self:
return self._confidence
return int(self._confidence * self.parent.cumulative_confidence / 100)
@property
def resolved_hosts(self):
if is_ip(self.host):
return {
self.host,
}
return self._resolved_hosts
@data.setter
def data(self, data):
self._hash = None
self._data_hash = None
self._id = None
self.__host = None
self._port = None
self._data = data
@property
def internal(self):
return self._internal
@internal.setter
def internal(self, value):
"""
Marks the event as internal, excluding it from output but allowing normal exchange between scan modules.
Internal events are typically speculative and may not be interesting by themselves but can lead to
the discovery of interesting events. This method sets the `_internal` attribute to True and adds the
"internal" tag.
Examples of internal events include `OPEN_TCP_PORT`s from the `speculate` module,
`IP_ADDRESS`es from the `ipneighbor` module, or out-of-scope `DNS_NAME`s that originate
from DNS resolutions.
The purpose of internal events is to enable speculative/explorative discovery without cluttering
the console with irrelevant or uninteresting events.
"""
if value not in (True, False):
raise ValueError(f'"internal" must be boolean, not {type(value)}')
if value is True:
self.add_tag("internal")
else:
self.remove_tag("internal")
self._internal = value
@property
def host(self):
"""
An abbreviated representation of the data that allows comparison with other events.
For host types, this is a hostname.
This allows comparison of an email or a URL with a domain, and vice versa
bob@evilcorp.com --> evilcorp.com
https://evilcorp.com --> evilcorp.com
evilcorp.com:80 --> evilcorp.com
For IP_* types, this is an instantiated object representing the event's data
E.g. for IP_ADDRESS, it could be an ipaddress.IPv4Address() or IPv6Address() object
"""
if self.__host is None:
self.host = self._host()
return self.__host
@host.setter
def host(self, host):
if self._host_original is None:
self._host_original = host
self.__host = host
@property
def host_original(self):
"""
Original host data, in case it was changed due to a wildcard DNS, etc.
"""
if self._host_original is None:
return self.host
return self._host_original
@property
def host_filterable(self):
"""
A string version of the event that's used for regex-based blacklisting.
For example, the user can specify "REGEX:.*.evilcorp.com" in their blacklist, and this regex
will be applied against this property.
"""
parsed_url = getattr(self, "parsed_url", None)
if parsed_url is not None:
return parsed_url.geturl()
if self.host is not None:
return str(self.host)
return ""
@property
def port(self):
self.host
if getattr(self, "parsed_url", None):
if self.parsed_url.port is not None:
return self.parsed_url.port
elif self.parsed_url.scheme == "https":
return 443
elif self.parsed_url.scheme == "http":
return 80
return self._port
@property
def netloc(self):
if self.host and is_ip_type(self.host, network=False):
return make_netloc(self.host, self.port)
return None
@property
def host_stem(self):
"""
An abbreviated representation of hostname that removes the TLD
E.g. www.evilcorp.com --> www.evilcorp
"""
if self.host and type(self.host) == str:
return domain_stem(self.host)
else:
return f"{self.host}"
@property
def discovery_context(self):
return self._discovery_context
@discovery_context.setter
def discovery_context(self, context):
def replace(match):
s = match.group()
return s.format(module=self.module, event=self)
try:
self._discovery_context = self._discovery_context_regex.sub(replace, context)
except Exception as e:
log.trace(f"Error formatting discovery context for {self}: {e} (context: '{context}')")
self._discovery_context = context
@property
def discovery_path(self):
"""
This event's full discovery context, including those of all its parents
"""
discovery_path = []
if self.parent is not None and self.parent is not self:
discovery_path = self.parent.discovery_path
return discovery_path + [self.discovery_context]
@property
def parent_chain(self):
"""
This event's full discovery context, including those of all its parents
"""
parent_chain = []
if self.parent is not None and self.parent is not self:
parent_chain = self.parent.parent_chain
return parent_chain + [str(self.uuid)]
@property
def words(self):
if self.__words is None:
self.__words = set(self._words())
return self.__words
def _words(self):
return set()
@property
def tags(self):
return self._tags
@tags.setter
def tags(self, tags):
self._tags = set()
if isinstance(tags, str):
tags = (tags,)
for tag in tags:
self.add_tag(tag)
def add_tag(self, tag):
self._tags.add(tagify(tag))
def add_tags(self, tags):
for tag in set(tags):
self.add_tag(tag)
def remove_tag(self, tag):
with suppress(KeyError):
self._tags.remove(tagify(tag))
@property
def always_emit(self):
"""
If this returns True, the event will always be distributed to output modules regardless of scope distance
"""
always_emit_tags = any(t in self.tags for t in self._always_emit_tags)
no_host_information = not bool(self.host)
return self._always_emit or always_emit_tags or no_host_information
@property
def id(self):
"""
A uniquely identifiable hash of the event from the event type + a SHA1 of its data
"""
if self._id is None:
self._id = f"{self.type}:{self.data_hash.hex()}"
return self._id
@property
def uuid(self):
"""
A universally unique identifier for the event
"""
return f"{self.type}:{self._uuid}"
@property
def data_hash(self):
"""
A raw byte hash of the event's data
"""
if self._data_hash is None:
self._data_hash = sha1(self.data_id).digest()
return self._data_hash
@property
def scope_distance(self):
return self._scope_distance
@scope_distance.setter
def scope_distance(self, scope_distance):
"""
Setter for the scope_distance attribute, ensuring it only decreases.
The scope_distance attribute is designed to never increase; it can only be set to smaller values than
the current one. If a larger value is provided, it is ignored. The setter also updates the event's
tags to reflect the new scope distance.
Parameters:
scope_distance (int): The new scope distance to set, must be a non-negative integer.
Note:
The method will automatically update the relevant 'distance-' tags associated with the event.
"""
if scope_distance < 0:
raise ValueError(f"Invalid scope distance: {scope_distance}")
# ensure scope distance does not increase (only allow setting to smaller values)
if self.scope_distance is None:
new_scope_distance = scope_distance
else:
new_scope_distance = min(self.scope_distance, scope_distance)
if self._scope_distance != new_scope_distance:
# remove old scope distance tags
self._scope_distance = new_scope_distance
self.refresh_scope_tags()
# apply recursively to parent events
parent_scope_distance = getattr(self.parent, "scope_distance", None)
if parent_scope_distance is not None and self.parent is not self:
self.parent.scope_distance = new_scope_distance + 1
def refresh_scope_tags(self):
for t in list(self.tags):
if t.startswith("distance-"):
self.remove_tag(t)
if self.host:
if self.scope_distance == 0:
self.add_tag("in-scope")
self.remove_tag("affiliate")
else:
self.remove_tag("in-scope")
self.add_tag(f"distance-{self.scope_distance}")
@property
def scope_description(self):
"""
Returns a single word describing the scope of the event.
"in-scope" if the event is in scope, "affiliate" if it's an affiliate, otherwise "distance-{scope_distance}"
"""
if self.scope_distance == 0:
return "in-scope"
elif "affiliate" in self.tags:
return "affiliate"
return f"distance-{self.scope_distance}"
@property
def parent(self):
return self._parent
@parent.setter
def parent(self, parent):
"""
Setter for the parent attribute, ensuring it's a valid event and updating scope distance.
Sets the parent of the event and automatically adjusts the scope distance based on the parent event's
scope distance. The scope distance is incremented by 1 if the host of the parent event is different
from the current event's host.
Parameters:
parent (BaseEvent): The new parent event to set. Must be a valid event object.
Note:
If an invalid parent is provided and the event is not a dummy, a warning will be logged.
"""
if is_event(parent):
self._parent = parent
hosts_are_same = (self.host and parent.host) and (self.host == parent.host)
new_scope_distance = int(parent.scope_distance)
if self.host and parent.scope_distance is not None:
# only increment the scope distance if the host changes
if self._scope_distance_increment_same_host or not hosts_are_same:
new_scope_distance += 1
self.scope_distance = new_scope_distance
# inherit certain tags
if hosts_are_same:
# inherit web spider distance from parent
self.web_spider_distance = getattr(parent, "web_spider_distance", 0)
event_has_url = getattr(self, "parsed_url", None) is not None
for t in parent.tags:
if t in ("affiliate",):
self.add_tag(t)
elif t.startswith("mutation-"):
self.add_tag(t)
# only add these tags if the event has a URL
if event_has_url:
if t in ("spider-danger", "spider-max"):
self.add_tag(t)
elif not self._dummy:
log.warning(f"Tried to set invalid parent on {self}: (got: {repr(parent)} ({type(parent)}))")
@property
def children(self):
return []
@property
def parent_id(self):
parent_id = getattr(self.get_parent(), "id", None)
if parent_id is not None:
return parent_id
return self._parent_id
@property
def parent_uuid(self):
parent_uuid = getattr(self.get_parent(), "uuid", None)
if parent_uuid is not None:
return parent_uuid
return self._parent_uuid
@property
def validators(self):
"""
Depending on whether the scan attribute is accessible, return either a config-aware or non-config-aware validator
This exists to prevent a chicken-and-egg scenario during the creation of certain events such as URLs,
whose sanitization behavior is different depending on the config.
However, thanks to this property, validation can still work in the absence of a config.
"""
if self.scan is not None:
return self.scan.helpers.config_aware_validators
return validators
def get_parent(self):
"""
Takes into account events with the _omit flag
"""
if getattr(self.parent, "_omit", False):
return self.parent.get_parent()
return self.parent
def get_parents(self, omit=False, include_self=False):
parents = []
e = self
if include_self:
parents.append(self)
while 1:
if omit:
parent = e.get_parent()
else:
parent = e.parent
if parent is None:
break
if e == parent:
break
parents.append(parent)
e = parent
return parents
def clone(self):
# Create a shallow copy of the event first
cloned_event = copy(self)
# Re-assign a new UUID
cloned_event._uuid = uuid.uuid4()
return cloned_event
def _host(self):
return ""
def _sanitize_data(self, data):
"""
Validates and sanitizes the event's data during instantiation.
By default, uses the '_data_load' method to pre-process the data and then applies the '_data_validator'
to validate and create a sanitized dictionary. Raises a ValidationError if any of the validations fail.
Subclasses can override this method to provide custom validation logic.
Returns:
Any: The sanitized data.
Raises:
ValidationError: If the data fails to validate.
"""
data = self._data_load(data)
if self._data_validator is not None:
if not isinstance(data, dict):
raise ValidationError(f"data is not of type dict: {data}")
data = self._data_validator(**data).model_dump(exclude_none=True)
return self.sanitize_data(data)
def sanitize_data(self, data):
return data
@property
def data_human(self):
"""
Human representation of event.data
"""
return self._data_human()
def _data_human(self):
if isinstance(self.data, (dict, list)):
with suppress(Exception):
return json.dumps(self.data, sort_keys=True)
return smart_decode(self.data)
def _data_load(self, data):
"""
How to load the event data (JSON-decode it, etc.)
"""
return data
@property
def data_id(self):
"""
Representation of the event.data used to calculate the event's ID
"""
return self._data_id()
def _data_id(self):
return self.data
@property
def pretty_string(self):
"""
A human-friendly representation of the event's data. Used for graph representation.
If the event's data is a dictionary, the function will try to return a JSON-formatted string.
Otherwise, it will use smart_decode to convert the data into a string representation.
Override if necessary.
Returns:
str: The graphical representation of the event's data.
"""
return self._pretty_string()
def _pretty_string(self):
return self._data_human()
@property
def data_graph(self):
"""
Representation of event.data for neo4j graph nodes
"""
return self.pretty_string
@property
def data_json(self):
"""
JSON representation of event.data
"""
return self.data
def __contains__(self, other):
"""
Membership checks for Events.
Supports:
- some_event in other_event (event vs event)
- "host:port" in other_event (string coerced to an event)
"""
# Fast path: already an Event
if is_event(other):
other_event = other
else:
try:
other_event = make_event(other, dummy=True)
except ValidationError:
return False
# if hashes match
if other_event == self:
return True
# if hosts match (including subnet / domain containment)
if self.host and other_event.host:
if self.host == other_event.host:
return True
# hostnames and IPs
radixtarget = RadixTarget()
radixtarget.insert(self.host)
return bool(radixtarget.search(other_event.host))
return False
def json(self, mode="json", siem_friendly=False):
"""
Serializes the event object to a JSON-compatible dictionary.
By default, it includes attributes such as 'type', 'id', 'data', 'scope_distance', and others that are present.
Additional specific attributes can be serialized based on the mode specified.
Parameters:
mode (str): Specifies the data serialization mode. Default is "json". Other options include "graph", "human", and "id".
siem_friendly (bool): Whether to format the JSON in a way that's friendly to SIEM ingestion by Elastic, Splunk, etc. This ensures the value of "data" is always the same type (a dictionary).
Returns:
dict: JSON-serializable dictionary representation of the event object.
"""
j = {}
# type, ID, scope description
for i in ("type", "id", "uuid", "scope_description", "netloc"):
v = getattr(self, i, "")
if v:
j.update({i: str(v)})
# event data
data_attr = getattr(self, f"data_{mode}", None)
if data_attr is not None:
data = data_attr
else:
data = smart_decode(self.data)
if siem_friendly:
j["data"] = {self.type: data}
else:
j["data"] = data
# host, dns children
if self.host:
j["host"] = str(self.host)
j["resolved_hosts"] = sorted(str(h) for h in self.resolved_hosts)
j["dns_children"] = {k: list(v) for k, v in self.dns_children.items()}
if isinstance(self.port, int):
j["port"] = self.port
# web spider distance
web_spider_distance = getattr(self, "web_spider_distance", None)
if web_spider_distance is not None:
j["web_spider_distance"] = web_spider_distance
# scope distance
j["scope_distance"] = self.scope_distance
# scan
if self.scan:
j["scan"] = self.scan.id
# timestamp
j["timestamp"] = self.timestamp.isoformat()
# parent event
parent_id = self.parent_id
if parent_id:
j["parent"] = parent_id
parent_uuid = self.parent_uuid
if parent_uuid:
j["parent_uuid"] = parent_uuid
# tags
if self.tags:
j.update({"tags": list(self.tags)})
# parent module
if self.module:
j.update({"module": str(self.module)})
# sequence of modules that led to discovery
if self.module_sequence:
j.update({"module_sequence": str(self.module_sequence)})
# discovery context
j["discovery_context"] = self.discovery_context
j["discovery_path"] = self.discovery_path
j["parent_chain"] = self.parent_chain
# parameter envelopes
parameter_envelopes = getattr(self, "envelopes", None)
if parameter_envelopes is not None:
j["envelopes"] = parameter_envelopes.to_dict()
# normalize non-primitive python objects
for k, v in list(j.items()):
if k == "data":
continue
if type(v) not in (str, int, float, bool, list, dict, type(None)):
try:
j[k] = json.dumps(v, sort_keys=True)
except Exception:
j[k] = smart_decode(v)
return j
@staticmethod
def from_json(j):
"""
Convenience shortcut to create an Event object from a JSON-compatible dictionary.
Calls the `event_from_json()` function to deserialize the event.
Parameters:
j (dict): The JSON-compatible dictionary containing event data.
Returns:
Event: The deserialized Event object.
"""
return event_from_json(j)
@property
def module_sequence(self):
"""
Get a human-friendly string that represents the sequence of modules responsible for generating this event.
Includes the names of omitted parent events to provide a complete view of the module sequence leading to this event.
Returns:
str: The module sequence in human-friendly format.
"""
module_name = getattr(self.module, "name", "")
if getattr(self.parent, "_omit", False):
module_name = f"{self.parent.module_sequence}->{module_name}"
return module_name
@property
def module_priority(self):
if self._module_priority is None:
module = getattr(self, "module", None)
self._module_priority = int(max(1, min(5, getattr(module, "priority", 3))))
return self._module_priority
@module_priority.setter
def module_priority(self, priority):
self._module_priority = int(max(1, min(5, priority)))
@property
def priority(self):
if self._priority is None:
timestamp = self.timestamp.timestamp()
if self.parent.timestamp == self.timestamp:
self._priority = (timestamp,)
else:
self._priority = getattr(self.parent, "priority", ()) + (timestamp,)
return self._priority
@property
def type(self):
return self._type
@type.setter
def type(self, val):
self._type = val
self._hash = None
self._id = None
@property
def _host_size(self):
"""
Used for sorting events by their host size, so that parent ones (e.g. IP subnets) come first
"""
if self.host:
if isinstance(self.host, str):
# smaller domains should come first
return len(self.host)
else:
try:
# bigger IP subnets should come first
return -self.host.num_addresses
except AttributeError:
# IP addresses default to 1
return 1
return 0
def __iter__(self):
"""
For dict(event)
"""
yield from self.json().items()
def __lt__(self, other):
"""
For queue sorting
"""
return self.priority < getattr(other, "priority", (0,))
def __gt__(self, other):
"""
For queue sorting
"""
return self.priority > getattr(other, "priority", (0,))
def __eq__(self, other):
"""
Event equality is **only** defined between Event instances.
Equality is based on the event hash (derived from its id). Comparisons to
non-Event types raise a ValueError to make incorrect comparisons explicit.
"""
if not is_event(other):
raise ValueError("Event equality is only defined between Event instances")
return hash(self) == hash(other)
def __hash__(self):
if self._hash is None:
self._hash = hash(self.id)
return self._hash
def __str__(self):
max_event_len = 80
d = str(self.data).replace("\n", "\\n")
return f'{self.type}("{d[:max_event_len]}{("..." if len(d) > max_event_len else "")}", module={self.module}, tags={self.tags})'
def __repr__(self):
return str(self)
class SCAN(BaseEvent):
def _data_human(self):
return f"{self.data['name']} ({self.data['id']})"
@property
def discovery_path(self):
return []
@property
def parent_chain(self):
return []
class FINISHED(BaseEvent):
"""
Special signal event to indicate end of scan
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._priority = (999999999999999,)
class DefaultEvent(BaseEvent):
def sanitize_data(self, data):
return data
class DictEvent(BaseEvent):
def sanitize_data(self, data):
url = data.get("url", "")
if url:
self.parsed_url = self.validators.validate_url_parsed(url)
return data
def _data_load(self, data):
if isinstance(data, str):
return json.loads(data)
return data
class DictHostEvent(DictEvent):
def _host(self):
if isinstance(self.data, dict) and "host" in self.data:
return make_ip_type(self.data["host"])
else:
parsed = getattr(self, "parsed_url", None)
if parsed is not None:
return make_ip_type(parsed.hostname)
class ClosestHostEvent(DictHostEvent):
# if a host/path/url isn't specified, this event type grabs it from the closest parent
# inherited by FINDING and VULNERABILITY
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if not self.host:
for parent in self.get_parents(include_self=True):
# inherit closest URL
if "url" not in self.data:
parent_url = getattr(parent, "parsed_url", None)
if parent_url is not None:
self.data["url"] = parent_url.geturl()
# inherit closest path
if "path" not in self.data and isinstance(parent.data, dict) and not parent.type == "HTTP_RESPONSE":
parent_path = parent.data.get("path", None)
if parent_path is not None:
self.data["path"] = parent_path
# inherit closest host
if parent.host:
self.data["host"] = str(parent.host)
# we do this to refresh the hash
self.data = self.data
break
# die if we still haven't found a host
if not self.host and not self.data.get("path", ""):
raise ValueError(f"No host was found in event parents: {self.get_parents()}. Host must be specified!")
class DictPathEvent(DictEvent):
def sanitize_data(self, data):
new_data = dict(data)
new_data["path"] = str(new_data["path"])
file_blobs = getattr(self.scan, "_file_blobs", False)
folder_blobs = getattr(self.scan, "_folder_blobs", False)
blob = None
try:
self._data_path = Path(data["path"])
# prepend the scan's home dir if the path is relative
if not self._data_path.is_absolute():
self._data_path = self.scan.home / self._data_path
if self._data_path.is_file():
self.add_tag("file")
if file_blobs:
with open(self._data_path, "rb") as file:
blob = file.read()
elif self._data_path.is_dir():
self.add_tag("folder")
if folder_blobs:
blob = self._tar_directory(self._data_path)
except KeyError:
pass
if blob:
new_data["blob"] = base64.b64encode(blob).decode("utf-8")
return new_data
def _tar_directory(self, dir_path):
tar_buffer = io.BytesIO()
with tarfile.open(fileobj=tar_buffer, mode="w:gz") as tar:
# Add the entire directory to the tar archive
tar.add(dir_path, arcname=dir_path.name)
return tar_buffer.getvalue()
class ASN(DictEvent):
_always_emit = True
_quick_emit = True
class CODE_REPOSITORY(DictHostEvent):
_always_emit = True
class _data_validator(BaseModel):
url: str
_validate_url = field_validator("url")(validators.validate_url)
def _pretty_string(self):
return self.data["url"]
class IP_ADDRESS(BaseEvent):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
ip = ipaddress.ip_address(self.data)
self.add_tag(f"ipv{ip.version}")
if ip.is_private:
self.add_tag("private-ip")
self.dns_resolve_distance = getattr(self.parent, "dns_resolve_distance", 0)
def sanitize_data(self, data):
return validators.validate_host(data)
def _host(self):
return ipaddress.ip_address(self.data)
class DnsEvent(BaseEvent):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# prevent runaway DNS entries
self.dns_resolve_distance = 0
parent = getattr(self, "parent", None)
module = getattr(self, "module", None)
module_type = getattr(module, "_type", "")
parent_module = getattr(parent, "module", None)
parent_module_type = getattr(parent_module, "_type", "")
if module_type == "DNS":
self.dns_resolve_distance = getattr(parent, "dns_resolve_distance", 0)
if parent_module_type == "DNS":
self.dns_resolve_distance += 1
# self.add_tag(f"resolve-distance-{self.dns_resolve_distance}")
# tag subdomain / domain
if is_subdomain(self.host):
self.add_tag("subdomain")
elif is_domain(self.host):
self.add_tag("domain")
# tag private IP
try:
if self.host.is_private:
self.add_tag("private-ip")
except AttributeError:
pass
class IP_RANGE(DnsEvent):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.add_tag(f"ipv{self.host.version}")
def sanitize_data(self, data):
return str(ipaddress.ip_network(str(data), strict=False))
def _host(self):
return ipaddress.ip_network(self.data)
class DNS_NAME(DnsEvent):
def sanitize_data(self, data):
return validators.validate_host(data)
def _host(self):
return self.data
def _words(self):
stem = self.host_stem
if not is_ptr(stem):
split_stem = stem.split(".")
if split_stem:
leftmost_segment = split_stem[0]
if leftmost_segment == "_wildcard":
stem = ".".join(split_stem[1:])
if stem:
return extract_words(stem)
return set()
class OPEN_TCP_PORT(BaseEvent):
def sanitize_data(self, data):
return validators.validate_open_port(data)
def _host(self):
host, self._port = split_host_port(self.data)
return host
def _words(self):
if not is_ip(self.host) and not is_ptr(self.host):
return extract_words(self.host_stem)
return set()
class OPEN_UDP_PORT(OPEN_TCP_PORT):
pass
class URL_UNVERIFIED(BaseEvent):
_status_code_regex = re.compile(r"^status-(\d{1,3})$")
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.num_redirects = getattr(self.parent, "num_redirects", 0)
def _data_id(self):
data = super()._data_id()
# remove the querystring for URL/URL_UNVERIFIED events, because we will conditionally add it back in (based on settings)
if self.__class__.__name__.startswith("URL") and self.scan is not None:
prefix = data.split("?")[0]
# consider spider-danger tag when deduping
if "spider-danger" in self.tags:
prefix += "spider-danger"
if not self.scan.config.get("url_querystring_remove", True) and self.parsed_url.query:
query_dict = parse_qs(self.parsed_url.query)
if self.scan.config.get("url_querystring_collapse", True):
# Only consider parameter names in dedup (collapse values)
cleaned_query = "|".join(sorted(query_dict.keys()))
else:
# Consider parameter names and values in dedup
cleaned_query = "&".join(
f"{key}={','.join(sorted(values))}" for key, values in sorted(query_dict.items())
)
data = f"{prefix}:{self.parsed_url.scheme}:{self.parsed_url.netloc}:{self.parsed_url.path}:{cleaned_query}"
return data
def sanitize_data(self, data):
self.parsed_url = self.validators.validate_url_parsed(data)
# special handling of URL extensions
if self.parsed_url is not None:
url_path = self.parsed_url.path
if url_path:
parsed_path_lower = str(url_path).lower()
extension = get_file_extension(parsed_path_lower)
if extension:
self.url_extension = extension
self.add_tag(f"extension-{extension}")
# tag as dir or endpoint
if str(self.parsed_url.path).endswith("/"):
self.add_tag("dir")
else:
self.add_tag("endpoint")
data = self.parsed_url.geturl()
return data
def add_tag(self, tag):
self_url = getattr(self, "parsed_url", "")
self_host = getattr(self, "host", "")
# autoincrement web spider distance if the "spider-danger" tag is added
if tag == "spider-danger" and "spider-danger" not in self.tags and self_url and self_host:
parent_hosts_and_urls = set()
for p in self.get_parents():
# URL_UNVERIFIED events don't count because they haven't been visited yet
if p.type == "URL_UNVERIFIED":
continue
url = getattr(p, "parsed_url", "")
parent_hosts_and_urls.add((p.host, url))
# if there's a URL anywhere in our parent chain that's different from ours but shares our host, we're in dAnGeR
dangerous_parent = any(
p_host == self.host and p_url != self_url for p_host, p_url in parent_hosts_and_urls
)
if dangerous_parent:
# increment the web spider distance
if self.type == "URL_UNVERIFIED":
self.web_spider_distance += 1
if self.is_spider_max:
self.add_tag("spider-max")
super().add_tag(tag)
@property
def is_spider_max(self):
if self.scan:
depth = url_depth(self.parsed_url)
if (self.web_spider_distance > self.scan.web_spider_distance) or (depth > self.scan.web_spider_depth):
return True
return False
def with_port(self):
netloc_with_port = make_netloc(self.host, self.port)
return self.parsed_url._replace(netloc=netloc_with_port)
def _words(self):
first_elem = self.parsed_url.path.lstrip("/").split("/")[0]
if "." not in first_elem:
return extract_words(first_elem)
return set()
def _host(self):
return make_ip_type(self.parsed_url.hostname)
@property
def http_status(self):
for t in self.tags:
match = self._status_code_regex.match(t)
if match:
return int(match.groups()[0])
return 0
class URL(URL_UNVERIFIED):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if not self._dummy and not any(t.startswith("status-") for t in self.tags):
raise ValidationError(
'Must specify HTTP status tag for URL event, e.g. "status-200". Use URL_UNVERIFIED if the URL is unvisited.'
)
@property
def resolved_hosts(self):
# TODO: remove this when we rip out httpx
return {".".join(i.split("-")[1:]) for i in self.tags if i.startswith("ip-")}
@property
def pretty_string(self):
return self.data
class STORAGE_BUCKET(DictEvent, URL_UNVERIFIED):
_always_emit = True
_suppress_chain_dupes = True
class _data_validator(BaseModel):
name: str
url: str
_validate_url = field_validator("url")(validators.validate_url)
def sanitize_data(self, data):
data = super().sanitize_data(data)
data["name"] = data["name"].lower()
return data
def _words(self):
return self.data["name"]
class URL_HINT(URL_UNVERIFIED):
pass
class WEB_PARAMETER(DictHostEvent):
@property
def children(self):
# if we have any subparams, raise a new WEB_PARAMETER for each one
children = []
envelopes = getattr(self, "envelopes", None)
if envelopes is not None:
subparams = sorted(list(self.envelopes.get_subparams()))
if envelopes.selected_subparam is None:
current_subparam = subparams[0]
envelopes.selected_subparam = current_subparam[0]
if len(subparams) > 1:
for subparam, _ in subparams[1:]:
clone = self.clone()
clone.envelopes = deepcopy(envelopes)
clone.envelopes.selected_subparam = subparam
clone.parent = self
children.append(clone)
return children
def sanitize_data(self, data):
original_value = data.get("original_value", None)
if original_value is not None:
try:
envelopes = BaseEnvelope.detect(original_value)
setattr(self, "envelopes", envelopes)
except ValueError as e:
log.verbose(f"Error detecting envelopes for {self}: {e}")
return data
def _data_id(self):
# dedupe by url:name:param_type
url = self.data.get("url", "")
name = self.data.get("name", "")
param_type = self.data.get("type", "")
envelopes = getattr(self, "envelopes", "")
subparam = getattr(envelopes, "selected_subparam", "")
return f"{url}:{name}:{param_type}:{subparam}"
def _outgoing_dedup_hash(self, event):
return hash(
(
str(event.host),
event.data["url"],
event.data.get("name", ""),
event.data.get("type", ""),
event.data.get("envelopes", ""),
)
)
def _url(self):
return self.data["url"]
def __str__(self):
max_event_len = 200
d = str(self.data)
return f'{self.type}("{d[:max_event_len]}{("..." if len(d) > max_event_len else "")}", module={self.module}, tags={self.tags})'
class EMAIL_ADDRESS(BaseEvent):
def sanitize_data(self, data):
return validators.validate_email(data)
def _host(self):
data = str(self.data).rsplit("@", 1)[-1]
host, self._port = split_host_port(data)
return host
def _words(self):
return extract_words(self.host_stem)
class HTTP_RESPONSE(URL_UNVERIFIED, DictEvent):
gitextract_abbww2k9/ ├── .gitattributes ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ └── feature_request.md │ ├── dependabot.yml │ └── workflows/ │ ├── benchmark.yml │ ├── codeql.yml │ ├── distro_tests.yml │ ├── docs_updater.yml │ ├── tests.yml │ └── version_updater.yml ├── .gitignore ├── .gitmodules ├── .pre-commit-config.yaml ├── Dockerfile ├── Dockerfile.full ├── LICENSE ├── README.md ├── bbot/ │ ├── __init__.py │ ├── cli.py │ ├── core/ │ │ ├── __init__.py │ │ ├── config/ │ │ │ ├── __init__.py │ │ │ ├── files.py │ │ │ └── logger.py │ │ ├── core.py │ │ ├── engine.py │ │ ├── event/ │ │ │ ├── __init__.py │ │ │ ├── base.py │ │ │ └── helpers.py │ │ ├── flags.py │ │ ├── helpers/ │ │ │ ├── __init__.py │ │ │ ├── async_helpers.py │ │ │ ├── bloom.py │ │ │ ├── cache.py │ │ │ ├── command.py │ │ │ ├── depsinstaller/ │ │ │ │ ├── __init__.py │ │ │ │ ├── installer.py │ │ │ │ └── sudo_askpass.py │ │ │ ├── diff.py │ │ │ ├── dns/ │ │ │ │ ├── __init__.py │ │ │ │ ├── brute.py │ │ │ │ ├── dns.py │ │ │ │ ├── engine.py │ │ │ │ ├── helpers.py │ │ │ │ └── mock.py │ │ │ ├── files.py │ │ │ ├── git.py │ │ │ ├── helper.py │ │ │ ├── interactsh.py │ │ │ ├── libmagic.py │ │ │ ├── misc.py │ │ │ ├── names_generator.py │ │ │ ├── ntlm.py │ │ │ ├── process.py │ │ │ ├── ratelimiter.py │ │ │ ├── regex.py │ │ │ ├── regexes.py │ │ │ ├── url.py │ │ │ ├── validators.py │ │ │ ├── web/ │ │ │ │ ├── __init__.py │ │ │ │ ├── client.py │ │ │ │ ├── engine.py │ │ │ │ ├── envelopes.py │ │ │ │ ├── ssl_context.py │ │ │ │ └── web.py │ │ │ ├── wordcloud.py │ │ │ └── yara_helper.py │ │ ├── modules.py │ │ ├── multiprocess.py │ │ └── shared_deps.py │ ├── db/ │ │ └── sql/ │ │ └── models.py │ ├── defaults.yml │ ├── errors.py │ ├── logger.py │ ├── modules/ │ │ ├── __init__.py │ │ ├── ajaxpro.py │ │ ├── anubisdb.py │ │ ├── apkpure.py │ │ ├── aspnet_bin_exposure.py │ │ ├── azure_realm.py │ │ ├── azure_tenant.py │ │ ├── baddns.py │ │ ├── baddns_direct.py │ │ ├── baddns_zone.py │ │ ├── badsecrets.py │ │ ├── base.py │ │ ├── bevigil.py │ │ ├── bucket_amazon.py │ │ ├── bucket_digitalocean.py │ │ ├── bucket_file_enum.py │ │ ├── bucket_firebase.py │ │ ├── bucket_google.py │ │ ├── bucket_microsoft.py │ │ ├── bufferoverrun.py │ │ ├── builtwith.py │ │ ├── bypass403.py │ │ ├── c99.py │ │ ├── censys_dns.py │ │ ├── censys_ip.py │ │ ├── certspotter.py │ │ ├── chaos.py │ │ ├── code_repository.py │ │ ├── credshed.py │ │ ├── crt.py │ │ ├── crt_db.py │ │ ├── deadly/ │ │ │ └── legba.py │ │ ├── dehashed.py │ │ ├── digitorus.py │ │ ├── dnsbimi.py │ │ ├── dnsbrute.py │ │ ├── dnsbrute_mutations.py │ │ ├── dnscaa.py │ │ ├── dnscommonsrv.py │ │ ├── dnsdumpster.py │ │ ├── dnstlsrpt.py │ │ ├── docker_pull.py │ │ ├── dockerhub.py │ │ ├── dotnetnuke.py │ │ ├── emailformat.py │ │ ├── extractous.py │ │ ├── ffuf.py │ │ ├── ffuf_shortnames.py │ │ ├── filedownload.py │ │ ├── fingerprintx.py │ │ ├── fullhunt.py │ │ ├── generic_ssrf.py │ │ ├── git.py │ │ ├── git_clone.py │ │ ├── gitdumper.py │ │ ├── github_codesearch.py │ │ ├── github_org.py │ │ ├── github_usersearch.py │ │ ├── github_workflows.py │ │ ├── gitlab_com.py │ │ ├── gitlab_onprem.py │ │ ├── google_playstore.py │ │ ├── gowitness.py │ │ ├── graphql_introspection.py │ │ ├── hackertarget.py │ │ ├── host_header.py │ │ ├── httpx.py │ │ ├── hunt.py │ │ ├── hunterio.py │ │ ├── iis_shortnames.py │ │ ├── internal/ │ │ │ ├── __init__.py │ │ │ ├── aggregate.py │ │ │ ├── base.py │ │ │ ├── cloudcheck.py │ │ │ ├── dnsresolve.py │ │ │ ├── excavate.py │ │ │ ├── speculate.py │ │ │ └── unarchive.py │ │ ├── ip2location.py │ │ ├── ipneighbor.py │ │ ├── ipstack.py │ │ ├── jadx.py │ │ ├── leakix.py │ │ ├── lightfuzz/ │ │ │ ├── lightfuzz.py │ │ │ └── submodules/ │ │ │ ├── __init__.py │ │ │ ├── base.py │ │ │ ├── cmdi.py │ │ │ ├── crypto.py │ │ │ ├── esi.py │ │ │ ├── path.py │ │ │ ├── serial.py │ │ │ ├── sqli.py │ │ │ ├── ssti.py │ │ │ └── xss.py │ │ ├── medusa.py │ │ ├── myssl.py │ │ ├── newsletters.py │ │ ├── ntlm.py │ │ ├── nuclei.py │ │ ├── oauth.py │ │ ├── otx.py │ │ ├── output/ │ │ │ ├── __init__.py │ │ │ ├── asset_inventory.py │ │ │ ├── base.py │ │ │ ├── csv.py │ │ │ ├── discord.py │ │ │ ├── emails.py │ │ │ ├── http.py │ │ │ ├── json.py │ │ │ ├── mysql.py │ │ │ ├── neo4j.py │ │ │ ├── nmap_xml.py │ │ │ ├── postgres.py │ │ │ ├── python.py │ │ │ ├── slack.py │ │ │ ├── splunk.py │ │ │ ├── sqlite.py │ │ │ ├── stdout.py │ │ │ ├── subdomains.py │ │ │ ├── teams.py │ │ │ ├── txt.py │ │ │ ├── web_parameters.py │ │ │ ├── web_report.py │ │ │ └── websocket.py │ │ ├── paramminer_cookies.py │ │ ├── paramminer_getparams.py │ │ ├── paramminer_headers.py │ │ ├── passivetotal.py │ │ ├── pgp.py │ │ ├── portfilter.py │ │ ├── portscan.py │ │ ├── postman.py │ │ ├── postman_download.py │ │ ├── rapiddns.py │ │ ├── reflected_parameters.py │ │ ├── report/ │ │ │ ├── affiliates.py │ │ │ ├── asn.py │ │ │ └── base.py │ │ ├── retirejs.py │ │ ├── robots.py │ │ ├── securitytrails.py │ │ ├── securitytxt.py │ │ ├── shodan_dns.py │ │ ├── shodan_idb.py │ │ ├── sitedossier.py │ │ ├── skymem.py │ │ ├── smuggler.py │ │ ├── social.py │ │ ├── sslcert.py │ │ ├── subdomaincenter.py │ │ ├── subdomainradar.py │ │ ├── telerik.py │ │ ├── templates/ │ │ │ ├── bucket.py │ │ │ ├── censys.py │ │ │ ├── github.py │ │ │ ├── gitlab.py │ │ │ ├── postman.py │ │ │ ├── shodan.py │ │ │ ├── sql.py │ │ │ ├── subdomain_enum.py │ │ │ └── webhook.py │ │ ├── trickest.py │ │ ├── trufflehog.py │ │ ├── url_manipulation.py │ │ ├── urlscan.py │ │ ├── vhost.py │ │ ├── viewdns.py │ │ ├── virustotal.py │ │ ├── wafw00f.py │ │ ├── wayback.py │ │ └── wpscan.py │ ├── presets/ │ │ ├── baddns-intense.yml │ │ ├── cloud-enum.yml │ │ ├── code-enum.yml │ │ ├── email-enum.yml │ │ ├── fast.yml │ │ ├── kitchen-sink.yml │ │ ├── nuclei/ │ │ │ ├── nuclei-budget.yml │ │ │ ├── nuclei-intense.yml │ │ │ ├── nuclei-technology.yml │ │ │ └── nuclei.yml │ │ ├── spider-intense.yml │ │ ├── spider.yml │ │ ├── subdomain-enum.yml │ │ ├── tech-detect.yml │ │ ├── web/ │ │ │ ├── dirbust-heavy.yml │ │ │ ├── dirbust-light.yml │ │ │ ├── dotnet-audit.yml │ │ │ ├── iis-shortnames.yml │ │ │ ├── lightfuzz-heavy.yml │ │ │ ├── lightfuzz-light.yml │ │ │ ├── lightfuzz-medium.yml │ │ │ ├── lightfuzz-superheavy.yml │ │ │ ├── lightfuzz-xss.yml │ │ │ └── paramminer.yml │ │ ├── web-basic.yml │ │ ├── web-screenshots.yml │ │ └── web-thorough.yml │ ├── scanner/ │ │ ├── __init__.py │ │ ├── dispatcher.py │ │ ├── manager.py │ │ ├── preset/ │ │ │ ├── __init__.py │ │ │ ├── args.py │ │ │ ├── conditions.py │ │ │ ├── environ.py │ │ │ ├── path.py │ │ │ └── preset.py │ │ ├── scanner.py │ │ ├── stats.py │ │ └── target.py │ ├── scripts/ │ │ ├── benchmark_report.py │ │ └── docs.py │ ├── test/ │ │ ├── __init__.py │ │ ├── bbot_fixtures.py │ │ ├── benchmarks/ │ │ │ ├── __init__.py │ │ │ ├── test_bloom_filter_benchmarks.py │ │ │ ├── test_closest_match_benchmarks.py │ │ │ ├── test_event_validation_benchmarks.py │ │ │ ├── test_excavate_benchmarks.py │ │ │ ├── test_ipaddress_benchmarks.py │ │ │ └── test_weighted_shuffle_benchmarks.py │ │ ├── conftest.py │ │ ├── coverage.cfg │ │ ├── fastapi_test.py │ │ ├── owasp_mastg.apk │ │ ├── run_tests.sh │ │ ├── test.conf │ │ ├── test_output.ndjson │ │ ├── test_step_1/ │ │ │ ├── __init__.py │ │ │ ├── test__module__tests.py │ │ │ ├── test_bbot_fastapi.py │ │ │ ├── test_bloom_filter.py │ │ │ ├── test_cli.py │ │ │ ├── test_command.py │ │ │ ├── test_config.py │ │ │ ├── test_depsinstaller.py │ │ │ ├── test_dns.py │ │ │ ├── test_docs.py │ │ │ ├── test_engine.py │ │ │ ├── test_event_seeds.py │ │ │ ├── test_events.py │ │ │ ├── test_files.py │ │ │ ├── test_helpers.py │ │ │ ├── test_manager_deduplication.py │ │ │ ├── test_manager_scope_accuracy.py │ │ │ ├── test_modules_basic.py │ │ │ ├── test_presets.py │ │ │ ├── test_python_api.py │ │ │ ├── test_regexes.py │ │ │ ├── test_scan.py │ │ │ ├── test_scope.py │ │ │ ├── test_target.py │ │ │ ├── test_web.py │ │ │ └── test_web_envelopes.py │ │ ├── test_step_2/ │ │ │ ├── __init__.py │ │ │ ├── module_tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── base.py │ │ │ │ ├── test_module_affiliates.py │ │ │ │ ├── test_module_aggregate.py │ │ │ │ ├── test_module_ajaxpro.py │ │ │ │ ├── test_module_anubisdb.py │ │ │ │ ├── test_module_apkpure.py │ │ │ │ ├── test_module_asn.py │ │ │ │ ├── test_module_aspnet_bin_exposure.py │ │ │ │ ├── test_module_asset_inventory.py │ │ │ │ ├── test_module_azure_realm.py │ │ │ │ ├── test_module_azure_tenant.py │ │ │ │ ├── test_module_baddns.py │ │ │ │ ├── test_module_baddns_direct.py │ │ │ │ ├── test_module_baddns_zone.py │ │ │ │ ├── test_module_badsecrets.py │ │ │ │ ├── test_module_bevigil.py │ │ │ │ ├── test_module_bucket_amazon.py │ │ │ │ ├── test_module_bucket_digitalocean.py │ │ │ │ ├── test_module_bucket_file_enum.py │ │ │ │ ├── test_module_bucket_firebase.py │ │ │ │ ├── test_module_bucket_google.py │ │ │ │ ├── test_module_bucket_microsoft.py │ │ │ │ ├── test_module_bufferoverrun.py │ │ │ │ ├── test_module_builtwith.py │ │ │ │ ├── test_module_bypass403.py │ │ │ │ ├── test_module_c99.py │ │ │ │ ├── test_module_censys_dns.py │ │ │ │ ├── test_module_censys_ip.py │ │ │ │ ├── test_module_certspotter.py │ │ │ │ ├── test_module_chaos.py │ │ │ │ ├── test_module_cloudcheck.py │ │ │ │ ├── test_module_code_repository.py │ │ │ │ ├── test_module_credshed.py │ │ │ │ ├── test_module_crt.py │ │ │ │ ├── test_module_crt_db.py │ │ │ │ ├── test_module_csv.py │ │ │ │ ├── test_module_dehashed.py │ │ │ │ ├── test_module_digitorus.py │ │ │ │ ├── test_module_discord.py │ │ │ │ ├── test_module_dnsbimi.py │ │ │ │ ├── test_module_dnsbrute.py │ │ │ │ ├── test_module_dnsbrute_mutations.py │ │ │ │ ├── test_module_dnscaa.py │ │ │ │ ├── test_module_dnscommonsrv.py │ │ │ │ ├── test_module_dnsdumpster.py │ │ │ │ ├── test_module_dnsresolve.py │ │ │ │ ├── test_module_dnstlsrpt.py │ │ │ │ ├── test_module_docker_pull.py │ │ │ │ ├── test_module_dockerhub.py │ │ │ │ ├── test_module_dotnetnuke.py │ │ │ │ ├── test_module_emailformat.py │ │ │ │ ├── test_module_emails.py │ │ │ │ ├── test_module_excavate.py │ │ │ │ ├── test_module_extractous.py │ │ │ │ ├── test_module_ffuf.py │ │ │ │ ├── test_module_ffuf_shortnames.py │ │ │ │ ├── test_module_filedownload.py │ │ │ │ ├── test_module_fingerprintx.py │ │ │ │ ├── test_module_fullhunt.py │ │ │ │ ├── test_module_generic_ssrf.py │ │ │ │ ├── test_module_git.py │ │ │ │ ├── test_module_git_clone.py │ │ │ │ ├── test_module_gitdumper.py │ │ │ │ ├── test_module_github_codesearch.py │ │ │ │ ├── test_module_github_org.py │ │ │ │ ├── test_module_github_usersearch.py │ │ │ │ ├── test_module_github_workflows.py │ │ │ │ ├── test_module_gitlab_com.py │ │ │ │ ├── test_module_gitlab_onprem.py │ │ │ │ ├── test_module_google_playstore.py │ │ │ │ ├── test_module_gowitness.py │ │ │ │ ├── test_module_graphql_introspection.py │ │ │ │ ├── test_module_hackertarget.py │ │ │ │ ├── test_module_host_header.py │ │ │ │ ├── test_module_http.py │ │ │ │ ├── test_module_httpx.py │ │ │ │ ├── test_module_hunt.py │ │ │ │ ├── test_module_hunterio.py │ │ │ │ ├── test_module_iis_shortnames.py │ │ │ │ ├── test_module_ip2location.py │ │ │ │ ├── test_module_ipneighbor.py │ │ │ │ ├── test_module_ipstack.py │ │ │ │ ├── test_module_jadx.py │ │ │ │ ├── test_module_json.py │ │ │ │ ├── test_module_leakix.py │ │ │ │ ├── test_module_legba.py │ │ │ │ ├── test_module_lightfuzz.py │ │ │ │ ├── test_module_medusa.py │ │ │ │ ├── test_module_mysql.py │ │ │ │ ├── test_module_myssl.py │ │ │ │ ├── test_module_neo4j.py │ │ │ │ ├── test_module_newsletters.py │ │ │ │ ├── test_module_nmap_xml.py │ │ │ │ ├── test_module_ntlm.py │ │ │ │ ├── test_module_nuclei.py │ │ │ │ ├── test_module_oauth.py │ │ │ │ ├── test_module_otx.py │ │ │ │ ├── test_module_paramminer_cookies.py │ │ │ │ ├── test_module_paramminer_getparams.py │ │ │ │ ├── test_module_paramminer_headers.py │ │ │ │ ├── test_module_passivetotal.py │ │ │ │ ├── test_module_pgp.py │ │ │ │ ├── test_module_portfilter.py │ │ │ │ ├── test_module_portscan.py │ │ │ │ ├── test_module_postgres.py │ │ │ │ ├── test_module_postman.py │ │ │ │ ├── test_module_postman_download.py │ │ │ │ ├── test_module_python.py │ │ │ │ ├── test_module_rapiddns.py │ │ │ │ ├── test_module_reflected_parameters.py │ │ │ │ ├── test_module_retirejs.py │ │ │ │ ├── test_module_robots.py │ │ │ │ ├── test_module_securitytrails.py │ │ │ │ ├── test_module_securitytxt.py │ │ │ │ ├── test_module_shodan_dns.py │ │ │ │ ├── test_module_shodan_idb.py │ │ │ │ ├── test_module_sitedossier.py │ │ │ │ ├── test_module_skymem.py │ │ │ │ ├── test_module_slack.py │ │ │ │ ├── test_module_smuggler.py │ │ │ │ ├── test_module_social.py │ │ │ │ ├── test_module_speculate.py │ │ │ │ ├── test_module_splunk.py │ │ │ │ ├── test_module_sqlite.py │ │ │ │ ├── test_module_sslcert.py │ │ │ │ ├── test_module_stdout.py │ │ │ │ ├── test_module_subdomaincenter.py │ │ │ │ ├── test_module_subdomainradar.py │ │ │ │ ├── test_module_subdomains.py │ │ │ │ ├── test_module_teams.py │ │ │ │ ├── test_module_telerik.py │ │ │ │ ├── test_module_trickest.py │ │ │ │ ├── test_module_trufflehog.py │ │ │ │ ├── test_module_txt.py │ │ │ │ ├── test_module_unarchive.py │ │ │ │ ├── test_module_url_manipulation.py │ │ │ │ ├── test_module_urlscan.py │ │ │ │ ├── test_module_vhost.py │ │ │ │ ├── test_module_viewdns.py │ │ │ │ ├── test_module_virustotal.py │ │ │ │ ├── test_module_wafw00f.py │ │ │ │ ├── test_module_wayback.py │ │ │ │ ├── test_module_web_parameters.py │ │ │ │ ├── test_module_web_report.py │ │ │ │ ├── test_module_websocket.py │ │ │ │ └── test_module_wpscan.py │ │ │ └── template_tests/ │ │ │ ├── __init__.py │ │ │ └── test_template_subdomain_enum.py │ │ ├── testsslcert.pem │ │ └── testsslkey.pem │ └── wordlists/ │ ├── devops_mutations.txt │ ├── ms_on_prem_subdomains.txt │ ├── nameservers.txt │ ├── paramminer_headers.txt │ ├── paramminer_parameters.txt │ ├── raft-small-extensions-lowercase_CLEANED.txt │ ├── top_open_ports_nmap.txt │ └── valid_url_schemes.txt ├── bbot-docker.sh ├── codecov.yml ├── docs/ │ ├── comparison.md │ ├── contribution.md │ ├── data/ │ │ └── chord_graph/ │ │ ├── entities.json │ │ ├── rels.json │ │ └── vega.json │ ├── dev/ │ │ ├── architecture.md │ │ ├── basemodule.md │ │ ├── core.md │ │ ├── dev_environment.md │ │ ├── discord_bot.md │ │ ├── engine.md │ │ ├── event.md │ │ ├── helpers/ │ │ │ ├── command.md │ │ │ ├── dns.md │ │ │ ├── index.md │ │ │ ├── interactsh.md │ │ │ ├── misc.md │ │ │ ├── web.md │ │ │ └── wordcloud.md │ │ ├── index.md │ │ ├── module_howto.md │ │ ├── presets.md │ │ ├── scanner.md │ │ ├── target.md │ │ └── tests.md │ ├── diagrams/ │ │ ├── engine-architecture.drawio │ │ ├── event-flow.drawio │ │ └── module-recursion.drawio │ ├── how_it_works.md │ ├── index.md │ ├── javascripts/ │ │ ├── tablesort.js │ │ ├── vega-embed@6.js │ │ ├── vega-lite@5.js │ │ └── vega@5.js │ ├── modules/ │ │ ├── custom_yara_rules.md │ │ ├── internal_modules.md │ │ ├── lightfuzz.md │ │ ├── list_of_modules.md │ │ └── nuclei.md │ ├── release_history.md │ ├── scanning/ │ │ ├── advanced.md │ │ ├── configuration.md │ │ ├── events.md │ │ ├── index.md │ │ ├── output.md │ │ ├── presets.md │ │ ├── presets_list.md │ │ └── tips_and_tricks.md │ └── troubleshooting.md ├── examples/ │ └── discord_bot.py ├── extra_sass/ │ └── style.css.scss ├── funding.yml ├── mkdocs.yml └── pyproject.toml
Showing preview only (528K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (6187 symbols across 407 files)
FILE: bbot/cli.py
function _main (line 32) | async def _main():
function main (line 307) | def main():
FILE: bbot/core/config/files.py
class BBOTConfigFiles (line 12) | class BBOTConfigFiles:
method __init__ (line 18) | def __init__(self, core):
method _get_config (line 21) | def _get_config(self, filename, name="config"):
method get_custom_config (line 34) | def get_custom_config(self):
method get_default_config (line 40) | def get_default_config(self):
FILE: bbot/core/config/logger.py
class ColoredFormatter (line 18) | class ColoredFormatter(logging.Formatter):
method format (line 26) | def format(self, record):
class BBOTLogger (line 40) | class BBOTLogger:
method __init__ (line 48) | def __init__(self, core):
method cleanup_logging (line 80) | def cleanup_logging(self):
method setup_queue_handler (line 98) | def setup_queue_handler(self, logging_queue=None, log_level=logging.DE...
method addLoggingLevel (line 111) | def addLoggingLevel(self, levelName, levelNum, methodName=None):
method loggers (line 162) | def loggers(self):
method add_log_handler (line 170) | def add_log_handler(self, handler, formatter=None):
method remove_log_handler (line 178) | def remove_log_handler(self, handler):
method include_logger (line 186) | def include_logger(self, logger):
method stderr_filter (line 194) | def stderr_filter(self, record):
method log_handlers (line 202) | def log_handlers(self):
method log_level (line 236) | def log_level(self):
method log_level (line 242) | def log_level(self, level):
method set_log_level (line 245) | def set_log_level(self, level, logger=None):
method toggle_log_level (line 254) | def toggle_log_level(self, logger=None):
FILE: bbot/core/core.py
class BBOTCore (line 15) | class BBOTCore:
method __init__ (line 34) | def __init__(self):
method _prep_multiprocessing (line 47) | def _prep_multiprocessing(self):
method home (line 64) | def home(self):
method cache_dir (line 68) | def cache_dir(self):
method tools_dir (line 72) | def tools_dir(self):
method temp_dir (line 76) | def temp_dir(self):
method lib_dir (line 80) | def lib_dir(self):
method scans_dir (line 84) | def scans_dir(self):
method config (line 88) | def config(self):
method default_config (line 101) | def default_config(self):
method default_config (line 114) | def default_config(self, value):
method custom_config (line 123) | def custom_config(self):
method custom_config (line 134) | def custom_config(self, value):
method no_secrets_config (line 143) | def no_secrets_config(self, config):
method secrets_only_config (line 156) | def secrets_only_config(self, config):
method merge_custom (line 169) | def merge_custom(self, config):
method merge_default (line 175) | def merge_default(self, config):
method copy (line 181) | def copy(self):
method files_config (line 190) | def files_config(self):
method create_process (line 201) | def create_process(self, *args, **kwargs):
method create_thread (line 216) | def create_thread(self, *args, **kwargs):
method logger (line 222) | def logger(self):
FILE: bbot/core/engine.py
class EngineBase (line 28) | class EngineBase:
method __init__ (line 45) | def __init__(self, debug=False):
method pickle (line 50) | def pickle(self, obj):
method unpickle (line 58) | def unpickle(self, binary):
method _infinite_retry (line 67) | async def _infinite_retry(self, callback, *args, **kwargs):
method engine_debug (line 84) | def engine_debug(self, *args, **kwargs):
class EngineClient (line 89) | class EngineClient(EngineBase):
method __init__ (line 124) | def __init__(self, debug=False, **kwargs):
method check_error (line 141) | def check_error(self, message):
method run_and_return (line 151) | async def run_and_return(self, command, *args, **kwargs):
method run_and_yield (line 179) | async def run_and_yield(self, command, *args, **kwargs):
method send_cancel_message (line 215) | async def send_cancel_message(self, socket, context):
method send_shutdown_message (line 232) | async def send_shutdown_message(self):
method check_stop (line 247) | def check_stop(self, message):
method make_message (line 252) | def make_message(self, command, args=None, kwargs=None):
method available_commands (line 265) | def available_commands(self):
method start_server (line 268) | def start_server(self):
method server_process (line 294) | def server_process(server_class, socket_path, **kwargs):
method new_socket (line 312) | async def new_socket(self):
method shutdown (line 331) | async def shutdown(self):
class EngineServer (line 350) | class EngineServer(EngineBase):
method __init__ (line 379) | def __init__(self, socket_path, debug=False):
method client_id_context (line 403) | def client_id_context(self, value):
method run_and_return (line 410) | async def run_and_return(self, client_id, command_fn, *args, **kwargs):
method run_and_yield (line 440) | async def run_and_yield(self, client_id, command_fn, *args, **kwargs):
method send_socket_multipart (line 473) | async def send_socket_multipart(self, client_id, message):
method check_error (line 481) | def check_error(self, message):
method worker (line 485) | async def worker(self):
method _shutdown (line 549) | async def _shutdown(self):
method task_pool (line 566) | async def task_pool(self, fn, args_kwargs, threads=10, timeout=300, gl...
method new_child_task (line 607) | def new_child_task(self, coro):
method finished_tasks (line 633) | async def finished_tasks(self, tasks, timeout=None):
method cancel_task (line 654) | async def cancel_task(self, client_id):
method _await_cancelled_task (line 670) | async def _await_cancelled_task(self, task):
method cancel_all_tasks (line 682) | async def cancel_all_tasks(self):
FILE: bbot/core/event/base.py
class BaseEvent (line 49) | class BaseEvent:
method __init__ (line 173) | def __init__(
method data (line 285) | def data(self):
method confidence (line 289) | def confidence(self):
method confidence (line 293) | def confidence(self, confidence):
method cumulative_confidence (line 297) | def cumulative_confidence(self):
method resolved_hosts (line 310) | def resolved_hosts(self):
method data (line 318) | def data(self, data):
method internal (line 327) | def internal(self):
method internal (line 331) | def internal(self, value):
method host (line 355) | def host(self):
method host (line 372) | def host(self, host):
method host_original (line 378) | def host_original(self):
method host_filterable (line 387) | def host_filterable(self):
method port (line 402) | def port(self):
method netloc (line 414) | def netloc(self):
method host_stem (line 420) | def host_stem(self):
method discovery_context (line 431) | def discovery_context(self):
method discovery_context (line 435) | def discovery_context(self, context):
method discovery_path (line 447) | def discovery_path(self):
method parent_chain (line 457) | def parent_chain(self):
method words (line 467) | def words(self):
method _words (line 472) | def _words(self):
method tags (line 476) | def tags(self):
method tags (line 480) | def tags(self, tags):
method add_tag (line 487) | def add_tag(self, tag):
method add_tags (line 490) | def add_tags(self, tags):
method remove_tag (line 494) | def remove_tag(self, tag):
method always_emit (line 499) | def always_emit(self):
method id (line 508) | def id(self):
method uuid (line 517) | def uuid(self):
method data_hash (line 524) | def data_hash(self):
method scope_distance (line 533) | def scope_distance(self):
method scope_distance (line 537) | def scope_distance(self, scope_distance):
method refresh_scope_tags (line 567) | def refresh_scope_tags(self):
method scope_description (line 580) | def scope_description(self):
method parent (line 593) | def parent(self):
method parent (line 597) | def parent(self, parent):
method children (line 638) | def children(self):
method parent_id (line 642) | def parent_id(self):
method parent_uuid (line 649) | def parent_uuid(self):
method validators (line 656) | def validators(self):
method get_parent (line 669) | def get_parent(self):
method get_parents (line 677) | def get_parents(self, omit=False, include_self=False):
method clone (line 695) | def clone(self):
method _host (line 702) | def _host(self):
method _sanitize_data (line 705) | def _sanitize_data(self, data):
method sanitize_data (line 726) | def sanitize_data(self, data):
method data_human (line 730) | def data_human(self):
method _data_human (line 736) | def _data_human(self):
method _data_load (line 742) | def _data_load(self, data):
method data_id (line 749) | def data_id(self):
method _data_id (line 755) | def _data_id(self):
method pretty_string (line 759) | def pretty_string(self):
method _pretty_string (line 773) | def _pretty_string(self):
method data_graph (line 777) | def data_graph(self):
method data_json (line 784) | def data_json(self):
method __contains__ (line 790) | def __contains__(self, other):
method json (line 820) | def json(self, mode="json", siem_friendly=False):
method from_json (line 907) | def from_json(j):
method module_sequence (line 922) | def module_sequence(self):
method module_priority (line 937) | def module_priority(self):
method module_priority (line 944) | def module_priority(self, priority):
method priority (line 948) | def priority(self):
method type (line 959) | def type(self):
method type (line 963) | def type(self, val):
method _host_size (line 969) | def _host_size(self):
method __iter__ (line 986) | def __iter__(self):
method __lt__ (line 992) | def __lt__(self, other):
method __gt__ (line 998) | def __gt__(self, other):
method __eq__ (line 1004) | def __eq__(self, other):
method __hash__ (line 1015) | def __hash__(self):
method __str__ (line 1020) | def __str__(self):
method __repr__ (line 1025) | def __repr__(self):
class SCAN (line 1029) | class SCAN(BaseEvent):
method _data_human (line 1030) | def _data_human(self):
method discovery_path (line 1034) | def discovery_path(self):
method parent_chain (line 1038) | def parent_chain(self):
class FINISHED (line 1042) | class FINISHED(BaseEvent):
method __init__ (line 1047) | def __init__(self, *args, **kwargs):
class DefaultEvent (line 1052) | class DefaultEvent(BaseEvent):
method sanitize_data (line 1053) | def sanitize_data(self, data):
class DictEvent (line 1057) | class DictEvent(BaseEvent):
method sanitize_data (line 1058) | def sanitize_data(self, data):
method _data_load (line 1064) | def _data_load(self, data):
class DictHostEvent (line 1070) | class DictHostEvent(DictEvent):
method _host (line 1071) | def _host(self):
class ClosestHostEvent (line 1080) | class ClosestHostEvent(DictHostEvent):
method __init__ (line 1083) | def __init__(self, *args, **kwargs):
class DictPathEvent (line 1108) | class DictPathEvent(DictEvent):
method sanitize_data (line 1109) | def sanitize_data(self, data):
method _tar_directory (line 1136) | def _tar_directory(self, dir_path):
class ASN (line 1144) | class ASN(DictEvent):
class CODE_REPOSITORY (line 1149) | class CODE_REPOSITORY(DictHostEvent):
class _data_validator (line 1152) | class _data_validator(BaseModel):
method _pretty_string (line 1156) | def _pretty_string(self):
class IP_ADDRESS (line 1160) | class IP_ADDRESS(BaseEvent):
method __init__ (line 1161) | def __init__(self, *args, **kwargs):
method sanitize_data (line 1169) | def sanitize_data(self, data):
method _host (line 1172) | def _host(self):
class DnsEvent (line 1176) | class DnsEvent(BaseEvent):
method __init__ (line 1177) | def __init__(self, *args, **kwargs):
class IP_RANGE (line 1204) | class IP_RANGE(DnsEvent):
method __init__ (line 1205) | def __init__(self, *args, **kwargs):
method sanitize_data (line 1209) | def sanitize_data(self, data):
method _host (line 1212) | def _host(self):
class DNS_NAME (line 1216) | class DNS_NAME(DnsEvent):
method sanitize_data (line 1217) | def sanitize_data(self, data):
method _host (line 1220) | def _host(self):
method _words (line 1223) | def _words(self):
class OPEN_TCP_PORT (line 1236) | class OPEN_TCP_PORT(BaseEvent):
method sanitize_data (line 1237) | def sanitize_data(self, data):
method _host (line 1240) | def _host(self):
method _words (line 1244) | def _words(self):
class OPEN_UDP_PORT (line 1250) | class OPEN_UDP_PORT(OPEN_TCP_PORT):
class URL_UNVERIFIED (line 1254) | class URL_UNVERIFIED(BaseEvent):
method __init__ (line 1257) | def __init__(self, *args, **kwargs):
method _data_id (line 1261) | def _data_id(self):
method sanitize_data (line 1285) | def sanitize_data(self, data):
method add_tag (line 1307) | def add_tag(self, tag):
method is_spider_max (line 1332) | def is_spider_max(self):
method with_port (line 1339) | def with_port(self):
method _words (line 1343) | def _words(self):
method _host (line 1349) | def _host(self):
method http_status (line 1353) | def http_status(self):
class URL (line 1361) | class URL(URL_UNVERIFIED):
method __init__ (line 1362) | def __init__(self, *args, **kwargs):
method resolved_hosts (line 1371) | def resolved_hosts(self):
method pretty_string (line 1376) | def pretty_string(self):
class STORAGE_BUCKET (line 1380) | class STORAGE_BUCKET(DictEvent, URL_UNVERIFIED):
class _data_validator (line 1384) | class _data_validator(BaseModel):
method sanitize_data (line 1389) | def sanitize_data(self, data):
method _words (line 1394) | def _words(self):
class URL_HINT (line 1398) | class URL_HINT(URL_UNVERIFIED):
class WEB_PARAMETER (line 1402) | class WEB_PARAMETER(DictHostEvent):
method children (line 1404) | def children(self):
method sanitize_data (line 1423) | def sanitize_data(self, data):
method _data_id (line 1433) | def _data_id(self):
method _outgoing_dedup_hash (line 1443) | def _outgoing_dedup_hash(self, event):
method _url (line 1454) | def _url(self):
method __str__ (line 1457) | def __str__(self):
class EMAIL_ADDRESS (line 1463) | class EMAIL_ADDRESS(BaseEvent):
method sanitize_data (line 1464) | def sanitize_data(self, data):
method _host (line 1467) | def _host(self):
method _words (line 1472) | def _words(self):
class HTTP_RESPONSE (line 1476) | class HTTP_RESPONSE(URL_UNVERIFIED, DictEvent):
method __init__ (line 1477) | def __init__(self, *args, **kwargs):
method _data_id (line 1484) | def _data_id(self):
method sanitize_data (line 1487) | def sanitize_data(self, data):
method _words (line 1515) | def _words(self):
method _pretty_string (line 1518) | def _pretty_string(self):
method raw_response (line 1522) | def raw_response(self):
method http_status (line 1531) | def http_status(self):
method http_title (line 1538) | def http_title(self):
method redirect_location (line 1546) | def redirect_location(self):
class VULNERABILITY (line 1559) | class VULNERABILITY(ClosestHostEvent):
method sanitize_data (line 1570) | def sanitize_data(self, data):
class _data_validator (line 1574) | class _data_validator(BaseModel):
method _pretty_string (line 1584) | def _pretty_string(self):
class FINDING (line 1588) | class FINDING(ClosestHostEvent):
class _data_validator (line 1592) | class _data_validator(BaseModel):
method _pretty_string (line 1600) | def _pretty_string(self):
class TECHNOLOGY (line 1604) | class TECHNOLOGY(DictHostEvent):
class _data_validator (line 1605) | class _data_validator(BaseModel):
method _data_id (line 1612) | def _data_id(self):
method _pretty_string (line 1617) | def _pretty_string(self):
class VHOST (line 1621) | class VHOST(DictHostEvent):
class _data_validator (line 1622) | class _data_validator(BaseModel):
method _pretty_string (line 1629) | def _pretty_string(self):
class PROTOCOL (line 1633) | class PROTOCOL(DictHostEvent):
class _data_validator (line 1634) | class _data_validator(BaseModel):
method sanitize_data (line 1642) | def sanitize_data(self, data):
method port (line 1648) | def port(self):
method _pretty_string (line 1651) | def _pretty_string(self):
class GEOLOCATION (line 1655) | class GEOLOCATION(BaseEvent):
class PASSWORD (line 1660) | class PASSWORD(BaseEvent):
class HASHED_PASSWORD (line 1665) | class HASHED_PASSWORD(BaseEvent):
class USERNAME (line 1670) | class USERNAME(BaseEvent):
class SOCIAL (line 1675) | class SOCIAL(DictHostEvent):
class WEBSCREENSHOT (line 1681) | class WEBSCREENSHOT(DictPathEvent, DictHostEvent):
class AZURE_TENANT (line 1686) | class AZURE_TENANT(DictEvent):
class WAF (line 1691) | class WAF(DictHostEvent):
class _data_validator (line 1695) | class _data_validator(BaseModel):
method _pretty_string (line 1703) | def _pretty_string(self):
class FILESYSTEM (line 1707) | class FILESYSTEM(DictPathEvent):
method __init__ (line 1708) | def __init__(self, *args, **kwargs):
class RAW_DNS_RECORD (line 1732) | class RAW_DNS_RECORD(DictHostEvent, DnsEvent):
class MOBILE_APP (line 1737) | class MOBILE_APP(DictEvent):
method _sanitize_data (line 1740) | def _sanitize_data(self, data):
method _pretty_string (line 1761) | def _pretty_string(self):
function update_event (line 1765) | def update_event(
function make_event (line 1814) | def make_event(
function event_from_json (line 1921) | def event_from_json(j, siem_friendly=False):
function is_event (line 1978) | def is_event(e):
FILE: bbot/core/event/helpers.py
class EventSeedRegistry (line 24) | class EventSeedRegistry(type):
method __new__ (line 29) | def __new__(mcs, name, bases, attrs):
function EventSeed (line 38) | def EventSeed(input):
class BaseEventSeed (line 53) | class BaseEventSeed(metaclass=EventSeedRegistry):
method __init__ (line 59) | def __init__(self, data):
method handle_match (line 64) | def handle_match(match):
method _sanitize_and_extract_host (line 70) | def _sanitize_and_extract_host(self, data):
method _override_input (line 79) | def _override_input(self, input):
method type (line 83) | def type(self):
method _hash (line 87) | def _hash(self):
method __hash__ (line 90) | def __hash__(self):
method __eq__ (line 93) | def __eq__(self, other):
method __str__ (line 96) | def __str__(self):
method __repr__ (line 99) | def __repr__(self):
class IP_ADDRESS (line 103) | class IP_ADDRESS(BaseEventSeed):
method precheck (line 107) | def precheck(data):
method _sanitize_and_extract_host (line 114) | def _sanitize_and_extract_host(data):
class DNS_NAME (line 119) | class DNS_NAME(BaseEventSeed):
method _sanitize_and_extract_host (line 123) | def _sanitize_and_extract_host(data):
class IP_RANGE (line 128) | class IP_RANGE(BaseEventSeed):
method precheck (line 132) | def precheck(data):
method _sanitize_and_extract_host (line 139) | def _sanitize_and_extract_host(data):
class OPEN_TCP_PORT (line 144) | class OPEN_TCP_PORT(BaseEventSeed):
method _sanitize_and_extract_host (line 148) | def _sanitize_and_extract_host(data):
class URL_UNVERIFIED (line 155) | class URL_UNVERIFIED(BaseEventSeed):
method _sanitize_and_extract_host (line 164) | def _sanitize_and_extract_host(data):
class EMAIL_ADDRESS (line 174) | class EMAIL_ADDRESS(BaseEventSeed):
method _sanitize_and_extract_host (line 178) | def _sanitize_and_extract_host(data):
class ORG_STUB (line 185) | class ORG_STUB(BaseEventSeed):
method _override_input (line 188) | def _override_input(self, input):
method handle_match (line 192) | def handle_match(match):
class USERNAME (line 196) | class USERNAME(BaseEventSeed):
method _override_input (line 199) | def _override_input(self, input):
method handle_match (line 203) | def handle_match(match):
class FILESYSTEM (line 207) | class FILESYSTEM(BaseEventSeed):
method _override_input (line 210) | def _override_input(self, input):
method handle_match (line 214) | def handle_match(match):
class MOBILE_APP (line 218) | class MOBILE_APP(BaseEventSeed):
method _override_input (line 221) | def _override_input(self, input):
method handle_match (line 225) | def handle_match(match):
class BLACKLIST_REGEX (line 229) | class BLACKLIST_REGEX(BaseEventSeed):
method _override_input (line 233) | def _override_input(self, input):
method handle_match (line 237) | def handle_match(match):
FILE: bbot/core/helpers/async_helpers.py
class ShuffleQueue (line 14) | class ShuffleQueue(asyncio.Queue):
method _put (line 15) | def _put(self, item):
method _get (line 19) | def _get(self):
class _Lock (line 23) | class _Lock(asyncio.Lock):
method __init__ (line 24) | def __init__(self, name):
class NamedLock (line 29) | class NamedLock:
method __init__ (line 37) | def __init__(self, max_size=10000):
method lock (line 41) | async def lock(self, name):
class TaskCounter (line 51) | class TaskCounter:
method __init__ (line 52) | def __init__(self):
method value (line 57) | def value(self):
method lock (line 61) | def lock(self):
method count (line 66) | def count(self, task_name, n=1, asyncio_task=None, _log=True):
class Task (line 71) | class Task:
method __init__ (line 72) | def __init__(self, manager, task_name, n=1, _log=True, asyncio_task=...
method __aenter__ (line 81) | async def __aenter__(self):
method __aexit__ (line 90) | async def __aexit__(self, exc_type, exc_val, exc_tb):
method asyncio_task (line 97) | def asyncio_task(self):
method function_name (line 103) | def function_name(self):
method cancel (line 108) | async def cancel(self):
method running_for (line 114) | def running_for(self):
method __str__ (line 117) | def __str__(self):
function get_event_loop (line 121) | def get_event_loop():
function async_to_sync_gen (line 129) | def async_to_sync_gen(async_gen):
function async_cachedmethod (line 138) | def async_cachedmethod(cache, key=keys.hashkey):
FILE: bbot/core/helpers/bloom.py
class BloomFilter (line 7) | class BloomFilter:
method __init__ (line 19) | def __init__(self, size=8000000):
method add (line 33) | def add(self, item):
method check (line 40) | def check(self, item):
method clear_all_bits (line 49) | def clear_all_bits(self):
method _hashes (line 54) | def _hashes(self, item):
method close (line 66) | def close(self):
method __del__ (line 70) | def __del__(self):
method __contains__ (line 76) | def __contains__(self, item):
FILE: bbot/core/helpers/cache.py
function cache_get (line 10) | def cache_get(self, key, text=True, cache_hrs=24 * 7):
function cache_put (line 30) | def cache_put(self, key, content):
function is_cached (line 44) | def is_cached(self, key, cache_hrs=24 * 7):
function cache_filename (line 52) | def cache_filename(self, key):
FILE: bbot/core/helpers/command.py
function run (line 13) | async def run(self, *command, check=False, text=True, idle_timeout=None,...
function run_live (line 79) | async def run_live(self, *command, check=False, text=True, idle_timeout=...
function _spawn_proc (line 163) | async def _spawn_proc(self, *command, **kwargs):
function _write_proc_line (line 208) | async def _write_proc_line(proc, chunk):
function _write_stdin (line 222) | async def _write_stdin(proc, _input):
function _prepare_command_kwargs (line 250) | def _prepare_command_kwargs(self, command, kwargs):
FILE: bbot/core/helpers/depsinstaller/installer.py
class DepsInstaller (line 25) | class DepsInstaller:
method __init__ (line 113) | def __init__(self, parent_helper):
method install (line 153) | async def install(self, *modules):
method install_module (line 226) | async def install_module(self, module):
method pip_install (line 267) | async def pip_install(self, packages, constraints=None):
method apt_install (line 294) | def apt_install(self, packages):
method _make_apt_ansible_args (line 310) | def _make_apt_ansible_args(self, packages):
method shell (line 325) | def shell(self, module, commands):
method tasks (line 347) | def tasks(self, module, tasks):
method ansible_run (line 356) | def ansible_run(self, tasks=None, module=None, args=None, ansible_args...
method read_setup_status (line 409) | def read_setup_status(self):
method write_setup_status (line 417) | def write_setup_status(self):
method ensure_root (line 421) | def ensure_root(self, message=""):
method install_core_deps (line 445) | async def install_core_deps(self):
method _setup_sudo_cache (line 502) | def _setup_sudo_cache(self):
method encrypted_sudo_pw (line 518) | def encrypted_sudo_pw(self):
method _encrypt_sudo_pw (line 523) | def _encrypt_sudo_pw(self, pw):
method _install_sudo_askpass (line 534) | def _install_sudo_askpass(self):
method all_modules_preloaded (line 544) | def all_modules_preloaded(self):
FILE: bbot/core/helpers/depsinstaller/sudo_askpass.py
function decrypt_password (line 12) | def decrypt_password(encrypted_data, key):
function main (line 21) | def main():
FILE: bbot/core/helpers/diff.py
class HttpCompare (line 11) | class HttpCompare:
method __init__ (line 12) | def __init__(
method merge_dictionaries (line 38) | def merge_dictionaries(headers1, headers2):
method _baseline (line 46) | async def _baseline(self):
method gen_cache_buster (line 132) | def gen_cache_buster(self):
method compare_headers (line 135) | def compare_headers(self, headers_1, headers_2):
method compare_body (line 156) | def compare_body(self, content_1, content_2):
method compare (line 174) | async def compare(
method canary_check (line 266) | async def canary_check(self, url, mode, rounds=3):
FILE: bbot/core/helpers/dns/brute.py
class DNSBrute (line 8) | class DNSBrute:
method __init__ (line 22) | def __init__(self, parent_helper):
method __call__ (line 34) | async def __call__(self, *args, **kwargs):
method dnsbrute_lock (line 38) | def dnsbrute_lock(self):
method dnsbrute (line 43) | async def dnsbrute(self, module, domain, subdomains, type=None):
method _massdns (line 85) | async def _massdns(self, module, domain, subdomains, rdtype):
method gen_subdomains (line 148) | async def gen_subdomains(self, prefixes, domain):
method resolver_file (line 154) | async def resolver_file(self):
method gen_random_subdomains (line 167) | def gen_random_subdomains(self, n=50):
method has_excessive_digits (line 180) | def has_excessive_digits(self, d):
FILE: bbot/core/helpers/dns/dns.py
class DNSHelper (line 18) | class DNSHelper(EngineClient):
method __init__ (line 53) | def __init__(self, parent_helper):
method resolve (line 86) | async def resolve(self, query, **kwargs):
method resolve_raw (line 89) | async def resolve_raw(self, query, **kwargs):
method resolve_batch (line 92) | async def resolve_batch(self, queries, **kwargs):
method resolve_raw_batch (line 101) | async def resolve_raw_batch(self, queries):
method brute (line 111) | def brute(self):
method is_wildcard (line 122) | async def is_wildcard(self, query, rdtypes, raw_dns_records=None):
method is_wildcard_domain (line 167) | async def is_wildcard_domain(self, domain, rdtypes):
method _wildcard_prevalidation (line 174) | def _wildcard_prevalidation(self, host):
method _mock_dns (line 195) | async def _mock_dns(self, mock_data, custom_lookup_fn=None):
FILE: bbot/core/helpers/dns/engine.py
class DNSEngine (line 26) | class DNSEngine(EngineServer):
method __init__ (line 37) | def __init__(self, socket_path, config={}, debug=False):
method resolve (line 89) | async def resolve(self, query, **kwargs):
method resolve_raw (line 124) | async def resolve_raw(self, query, **kwargs):
method _resolve_hostname (line 167) | async def _resolve_hostname(self, query, **kwargs):
method _resolve_ip (line 267) | async def _resolve_ip(self, query, **kwargs):
method resolve_batch (line 328) | async def resolve_batch(self, queries, threads=10, **kwargs):
method resolve_raw_batch (line 352) | async def resolve_raw_batch(self, queries, threads=10, **kwargs):
method _catch (line 361) | async def _catch(self, callback, *args, **kwargs):
method is_wildcard (line 394) | async def is_wildcard(self, query, rdtypes, raw_dns_records=None):
method is_wildcard_domain (line 517) | async def is_wildcard_domain(self, domain, rdtypes):
method _is_wildcard_zone (line 562) | async def _is_wildcard_zone(self, host, rdtype):
method _is_wildcard (line 603) | async def _is_wildcard(self, query, rdtypes, dns_children):
method dns_connectivity_lock (line 608) | def dns_connectivity_lock(self):
method _connectivity_check (line 613) | async def _connectivity_check(self, interval=5):
method debug (line 644) | def debug(self, *args, **kwargs):
method in_tests (line 649) | def in_tests(self):
method _mock_dns (line 652) | async def _mock_dns(self, mock_data, custom_lookup_fn=None):
FILE: bbot/core/helpers/dns/helpers.py
function extract_targets (line 157) | def extract_targets(record):
function service_record (line 212) | def service_record(host, rdtype=None):
FILE: bbot/core/helpers/dns/mock.py
class MockResolver (line 7) | class MockResolver:
method __init__ (line 8) | def __init__(self, mock_data=None, custom_lookup_fn=None):
method resolve_address (line 13) | async def resolve_address(self, ipaddr, *args, **kwargs):
method _lookup (line 19) | def _lookup(self, query, rdtype):
method create_dns_response (line 33) | def create_dns_response(self, query_name, answers, rdtype):
method resolve (line 52) | async def resolve(self, query_name, rdtype=None):
FILE: bbot/core/helpers/files.py
function tempfile (line 12) | def tempfile(self, content, pipe=True, extension=None):
function _feed_pipe (line 52) | def _feed_pipe(self, pipe, content, text=True):
function feed_pipe (line 96) | def feed_pipe(self, pipe, content, text=True):
function tempfile_tail (line 116) | def tempfile_tail(self, callback):
function tail (line 146) | def tail(filename, callback):
FILE: bbot/core/helpers/git.py
function sanitize_git_repo (line 4) | def sanitize_git_repo(repo_folder: Path):
FILE: bbot/core/helpers/helper.py
class ConfigAwareHelper (line 24) | class ConfigAwareHelper:
method __init__ (line 58) | def __init__(self, preset):
method dns (line 99) | def dns(self):
method web (line 105) | def web(self):
method cloudcheck (line 111) | def cloudcheck(self):
method bloom_filter (line 118) | def bloom_filter(self, size):
method interactsh (line 123) | def interactsh(self, *args, **kwargs):
method http_compare (line 126) | def http_compare(
method temp_filename (line 151) | def temp_filename(self, extension=None):
method clean_old_scans (line 160) | def clean_old_scans(self):
method make_target (line 166) | def make_target(self, *targets, **kwargs):
method config (line 170) | def config(self):
method web_config (line 174) | def web_config(self):
method scan (line 178) | def scan(self):
method loop (line 182) | def loop(self):
method run_in_executor (line 190) | def run_in_executor(self, callback, *args, **kwargs):
method run_in_executor_mp (line 201) | def run_in_executor_mp(self, callback, *args, **kwargs):
method in_tests (line 214) | def in_tests(self):
method __getattribute__ (line 217) | def __getattribute__(self, attr):
FILE: bbot/core/helpers/interactsh.py
class Interactsh (line 21) | class Interactsh:
method __init__ (line 81) | def __init__(self, parent_helper, poll_interval=10):
method register (line 90) | async def register(self, callback=None):
method deregister (line 169) | async def deregister(self):
method poll (line 202) | async def poll(self):
method poll_loop (line 256) | async def poll_loop(self, callback):
method _poll_loop (line 276) | async def _poll_loop(self, callback):
method _decrypt (line 294) | def _decrypt(self, aes_key, data):
FILE: bbot/core/helpers/libmagic.py
function get_magic_info (line 4) | def get_magic_info(file):
function get_compression (line 13) | def get_compression(mime_type):
FILE: bbot/core/helpers/misc.py
function is_domain (line 29) | def is_domain(d):
function is_subdomain (line 64) | def is_subdomain(d):
function is_ptr (line 99) | def is_ptr(d):
function is_url (line 122) | def is_url(u):
function is_uri (line 152) | def is_uri(u, return_scheme=False):
function split_host_port (line 187) | def split_host_port(d):
function parent_domain (line 258) | def parent_domain(d):
function domain_parents (line 293) | def domain_parents(d, include_self=False):
function subdomain_depth (line 328) | def subdomain_depth(d):
function parent_url (line 345) | def parent_url(u):
function url_parents (line 377) | def url_parents(u):
function best_http_status (line 407) | def best_http_status(code1, code2):
function tldextract (line 451) | def tldextract(data):
function split_domain (line 474) | def split_domain(hostname):
function domain_stem (line 503) | def domain_stem(domain):
function ip_network_parents (line 524) | def ip_network_parents(i, include_self=False):
function is_port (line 547) | def is_port(p):
function is_dns_name (line 568) | def is_dns_name(d):
function is_ip (line 594) | def is_ip(d, version=None, include_network=False):
function is_ip_type (line 630) | def is_ip_type(i, network=None):
function make_ip_type (line 658) | def make_ip_type(s):
function sha1 (line 692) | def sha1(data):
function smart_decode (line 713) | def smart_decode(data):
function smart_encode (line 735) | def smart_encode(data):
function ensure_utf8_compliant (line 760) | def ensure_utf8_compliant(text):
function recursive_decode (line 764) | def recursive_decode(data, max_depth=5):
function rand_string (line 806) | def rand_string(length=10, digits=True, numeric_only=False):
function truncate_string (line 838) | def truncate_string(s: str, n: int) -> str:
function extract_params_json (line 847) | def extract_params_json(json_data, compare_mode="getparam"):
function extract_params_xml (line 891) | def extract_params_xml(xml_data, compare_mode="getparam"):
function validate_parameter (line 942) | def validate_parameter(param, compare_mode):
function extract_words (line 952) | def extract_words(data, acronyms=True, wordninja=True, model=None, max_l...
function closest_match (line 1009) | def closest_match(s, choices, n=1, cutoff=0.0):
function get_closest_match (line 1040) | def get_closest_match(s, choices, msg=None):
function kill_children (line 1064) | def kill_children(parent_pid=None, sig=None):
function str_or_file (line 1093) | def str_or_file(s):
function chain_lists (line 1122) | def chain_lists(
function list_files (line 1183) | def list_files(directory, filter=lambda x: True):
function rm_at_exit (line 1207) | def rm_at_exit(path):
function delete_file (line 1221) | def delete_file(path):
function read_file (line 1237) | def read_file(filename):
function gen_numbers (line 1258) | def gen_numbers(n, padding=2):
function make_netloc (line 1285) | def make_netloc(host, port=None):
function which (line 1318) | def which(*executables, path=None):
function search_dict_by_key (line 1343) | def search_dict_by_key(key, d):
function search_format_dict (line 1368) | def search_format_dict(d, **kwargs):
function search_dict_values (line 1393) | def search_dict_values(d, *regexes):
function grouper (line 1435) | def grouper(iterable, n):
function split_list (line 1456) | def split_list(alist, wanted_parts=2):
function mkdir (line 1475) | def mkdir(path, check_writable=True, raise_error=True):
function make_date (line 1512) | def make_date(d=None, microseconds=False):
function error_and_exit (line 1539) | def error_and_exit(msg):
function get_file_extension (line 1544) | def get_file_extension(s):
function backup_file (line 1570) | def backup_file(filename, max_backups=10):
function latest_mtime (line 1605) | def latest_mtime(d):
function filesize (line 1633) | def filesize(f):
function rm_rf (line 1655) | def rm_rf(f, ignore_errors=False):
function clean_old (line 1669) | def clean_old(d, keep=10, filter=lambda x: True, key=latest_mtime, rever...
function extract_emails (line 1704) | def extract_emails(s):
function extract_host (line 1726) | def extract_host(s):
function smart_encode_punycode (line 1781) | def smart_encode_punycode(text: str) -> str:
function smart_decode_punycode (line 1799) | def smart_decode_punycode(text: str) -> str:
function can_sudo_without_password (line 1817) | def can_sudo_without_password():
function verify_sudo_password (line 1841) | def verify_sudo_password(sudo_pass):
function make_table (line 1870) | def make_table(rows, header, **kwargs):
function human_timedelta (line 1924) | def human_timedelta(d):
function bytes_to_human (line 1960) | def bytes_to_human(_bytes):
function human_to_bytes (line 1995) | def human_to_bytes(filesize):
function integer_to_ordinal (line 2035) | def integer_to_ordinal(n):
function cpu_architecture (line 2079) | def cpu_architecture():
function cpu_architecture_golang (line 2098) | def cpu_architecture_golang():
function cpu_architecture_rust (line 2112) | def cpu_architecture_rust():
function os_platform (line 2123) | def os_platform():
function os_platform_friendly (line 2141) | def os_platform_friendly():
function tagify (line 2163) | def tagify(s, delimiter=None, maxlen=None):
function memory_status (line 2188) | def memory_status():
function swap_status (line 2213) | def swap_status():
function get_size (line 2237) | def get_size(obj, max_depth=5, seen=None):
function is_file (line 2297) | def is_file(f):
function is_async_function (line 2319) | def is_async_function(f):
function execute_sync_or_async (line 2340) | async def execute_sync_or_async(callback, *args, **kwargs):
function get_exception_chain (line 2370) | def get_exception_chain(e):
function in_exception_chain (line 2397) | def in_exception_chain(e, exc_types):
function get_traceback_details (line 2418) | def get_traceback_details(e):
function cancel_tasks (line 2446) | async def cancel_tasks(tasks, ignore_errors=True):
function cancel_tasks_sync (line 2481) | def cancel_tasks_sync(tasks):
function weighted_shuffle (line 2504) | def weighted_shuffle(items, weights):
function parse_port_string (line 2551) | def parse_port_string(port_string):
function as_completed (line 2598) | async def as_completed(coros):
function clean_dns_record (line 2624) | def clean_dns_record(record):
function truncate_filename (line 2651) | def truncate_filename(file_path, max_length=255):
function get_keys_in_dot_syntax (line 2686) | def get_keys_in_dot_syntax(config):
function filter_dict (line 2728) | def filter_dict(d, *key_names, fuzzy=False, exclude_keys=None, _prev_key...
function clean_dict (line 2765) | def clean_dict(d, *key_names, fuzzy=False, exclude_keys=None, _prev_key=...
function calculate_entropy (line 2796) | def calculate_entropy(data):
function top_tcp_ports (line 2814) | def top_tcp_ports(n, as_string=False):
class SafeDict (line 2836) | class SafeDict(dict):
method __missing__ (line 2837) | def __missing__(self, key):
function safe_format (line 2841) | def safe_format(s, **kwargs):
function get_python_constraints (line 2848) | def get_python_constraints():
function is_printable (line 2866) | def is_printable(s):
FILE: bbot/core/helpers/names_generator.py
function random_name (line 707) | def random_name():
FILE: bbot/core/helpers/ntlm.py
class StrStruct (line 13) | class StrStruct(object):
method __init__ (line 14) | def __init__(self, pos_tup, raw):
function decode_ntlm_challenge (line 37) | def decode_ntlm_challenge(st):
function ntlmdecode (line 68) | def ntlmdecode(authenticate_header):
FILE: bbot/core/helpers/process.py
class BBOTThread (line 9) | class BBOTThread(threading.Thread):
method __init__ (line 12) | def __init__(self, *args, **kwargs):
method run (line 18) | def run(self):
class BBOTProcess (line 25) | class BBOTProcess(SpawnProcess):
method __init__ (line 28) | def __init__(self, *args, **kwargs):
method run (line 35) | def run(self):
FILE: bbot/core/helpers/ratelimiter.py
class RateLimiter (line 8) | class RateLimiter:
method __init__ (line 23) | def __init__(self, rate, name):
method lock (line 33) | def lock(self):
method __aenter__ (line 38) | async def __aenter__(self):
method __aexit__ (line 60) | async def __aexit__(self, exc_type, exc_val, exc_tb):
FILE: bbot/core/helpers/regex.py
class RegexHelper (line 6) | class RegexHelper:
method __init__ (line 17) | def __init__(self, parent_helper):
method ensure_compiled_regex (line 20) | def ensure_compiled_regex(self, r):
method compile (line 27) | def compile(self, *args, **kwargs):
method search (line 30) | async def search(self, compiled_regex, *args, **kwargs):
method match (line 34) | async def match(self, compiled_regex, *args, **kwargs):
method sub (line 38) | async def sub(self, compiled_regex, *args, **kwargs):
method findall (line 42) | async def findall(self, compiled_regex, *args, **kwargs):
method findall_multi (line 46) | async def findall_multi(self, compiled_regexes, *args, threads=10, **k...
method finditer (line 78) | async def finditer(self, compiled_regex, *args, **kwargs):
method finditer_multi (line 82) | async def finditer_multi(self, compiled_regexes, *args, **kwargs):
method _finditer_multi (line 90) | def _finditer_multi(self, compiled_regexes, *args, **kwargs):
method _finditer (line 97) | def _finditer(self, compiled_regex, *args, **kwargs):
method extract_params_html (line 100) | async def extract_params_html(self, *args, **kwargs):
method extract_emails (line 103) | async def extract_emails(self, *args, **kwargs):
method search_dict_values (line 106) | async def search_dict_values(self, *args, **kwargs):
method recursive_decode (line 112) | async def recursive_decode(self, *args, **kwargs):
FILE: bbot/core/helpers/url.py
function parse_url (line 12) | def parse_url(url):
function add_get_params (line 35) | def add_get_params(url, params, encode=True):
function get_get_params (line 78) | def get_get_params(url):
function charset (line 105) | def charset(p):
function param_type (line 145) | def param_type(p):
function hash_url (line 178) | def hash_url(url):
function url_depth (line 214) | def url_depth(url):
FILE: bbot/core/helpers/validators.py
function validator (line 15) | def validator(func):
function validate_port (line 42) | def validate_port(port: Union[str, int]):
function validate_open_port (line 69) | def validate_open_port(open_port: Union[str, int]):
function validate_host (line 78) | def validate_host(host: Union[str, ipaddress.IPv4Address, ipaddress.IPv6...
function validate_severity (line 133) | def validate_severity(severity: str):
function validate_email (line 141) | def validate_email(email: str):
function clean_url (line 148) | def clean_url(url: str, url_querystring_remove=True):
function collapse_urls (line 202) | def collapse_urls(*args, **kwargs):
function _collapse_urls (line 206) | def _collapse_urls(urls, threshold=10):
function validate_url (line 248) | def validate_url(url: str):
function validate_url_parsed (line 253) | def validate_url_parsed(url: str):
function soft_validate (line 260) | def soft_validate(s, t):
function is_email (line 294) | def is_email(email):
class Validators (line 302) | class Validators:
method __init__ (line 303) | def __init__(self, parent_helper):
method clean_url (line 306) | def clean_url(self, url: str):
method validate_url_parsed (line 310) | def validate_url_parsed(self, url: str):
FILE: bbot/core/helpers/web/client.py
class DummyCookies (line 8) | class DummyCookies(Cookies):
method extract_cookies (line 9) | def extract_cookies(self, *args, **kwargs):
class BBOTAsyncClient (line 13) | class BBOTAsyncClient(httpx.AsyncClient):
method from_config (line 32) | def from_config(cls, config, target, *args, **kwargs):
method __init__ (line 46) | def __init__(self, *args, **kwargs):
method build_request (line 87) | def build_request(self, *args, **kwargs):
method _merge_cookies (line 112) | def _merge_cookies(self, cookies):
method retries (line 118) | def retries(self):
FILE: bbot/core/helpers/web/engine.py
class HTTPEngine (line 16) | class HTTPEngine(EngineServer):
method __init__ (line 29) | def __init__(self, socket_path, target, config={}, debug=False):
method AsyncClient (line 39) | def AsyncClient(self, *args, **kwargs):
method request (line 52) | async def request(self, *args, **kwargs):
method request_batch (line 98) | async def request_batch(self, urls, threads=10, **kwargs):
method request_custom_batch (line 104) | async def request_custom_batch(self, urls_and_kwargs, threads=10, **kw...
method download (line 110) | async def download(self, url, **kwargs):
method stream_request (line 133) | async def stream_request(self, url, **kwargs):
method ssl_context_noverify (line 165) | def ssl_context_noverify(self):
method _acatch (line 177) | async def _acatch(self, url, raise_error):
FILE: bbot/core/helpers/web/envelopes.py
class EnvelopeChildTracker (line 21) | class EnvelopeChildTracker(type):
method __new__ (line 28) | def __new__(mcs, name, bases, class_dict):
class BaseEnvelope (line 38) | class BaseEnvelope(metaclass=EnvelopeChildTracker):
method __init__ (line 50) | def __init__(self, s):
method final_envelope (line 69) | def final_envelope(self):
method friendly_name (line 76) | def friendly_name(self):
method pack (line 82) | def pack(self, data=None):
method unpack (line 89) | def unpack(self, s):
method _pack (line 92) | def _pack(self, s):
method _unpack (line 98) | def _unpack(self, s):
method unpacked_data (line 104) | def unpacked_data(self, recursive=True):
method detect (line 115) | def detect(cls, s):
method get_subparams (line 136) | def get_subparams(self, key=None, data=None, recursive=True):
method get_subparam (line 152) | def get_subparam(self, key=None, recursive=True):
method pack_value (line 169) | def pack_value(self, value, key=None):
method set_subparam (line 199) | def set_subparam(self, key=None, value=None, recursive=True):
method name (line 221) | def name(self):
method num_envelopes (line 225) | def num_envelopes(self):
method summary (line 235) | def summary(self):
method to_dict (line 249) | def to_dict(self):
method __str__ (line 252) | def __str__(self):
class HexEnvelope (line 258) | class HexEnvelope(BaseEnvelope):
method _pack (line 267) | def _pack(self, s):
method _unpack (line 270) | def _unpack(self, s):
class B64Envelope (line 274) | class B64Envelope(BaseEnvelope):
method unpack (line 283) | def unpack(self, s):
method _pack (line 289) | def _pack(self, s):
method _unpack (line 292) | def _unpack(self, s):
class URLEnvelope (line 296) | class URLEnvelope(BaseEnvelope):
method unpack (line 303) | def unpack(self, s):
method _pack (line 309) | def _pack(self, s):
method _unpack (line 312) | def _unpack(self, s):
class TextEnvelope (line 316) | class TextEnvelope(BaseEnvelope):
method _pack (line 327) | def _pack(self, s):
method _unpack (line 330) | def _unpack(self, s):
class JSONEnvelope (line 351) | class JSONEnvelope(BaseEnvelope):
method _pack (line 361) | def _pack(self, s):
method _unpack (line 364) | def _unpack(self, s):
class XMLEnvelope (line 368) | class XMLEnvelope(BaseEnvelope):
method _pack (line 378) | def _pack(self, s):
method _unpack (line 381) | def _unpack(self, s):
FILE: bbot/core/helpers/web/web.py
class WebHelper (line 21) | class WebHelper(EngineClient):
method __init__ (line 48) | def __init__(self, parent_helper):
method AsyncClient (line 64) | def AsyncClient(self, *args, **kwargs):
method request (line 77) | async def request(self, *args, **kwargs):
method request_batch (line 132) | async def request_batch(self, urls, *args, **kwargs):
method request_custom_batch (line 154) | async def request_custom_batch(self, urls_and_kwargs):
method download (line 184) | async def download(self, url, **kwargs):
method wordlist (line 235) | async def wordlist(self, path, lines=None, zip=False, zip_filename=Non...
method curl (line 306) | async def curl(self, *args, **kwargs):
method beautifulsoup (line 434) | def beautifulsoup(
method response_to_json (line 492) | def response_to_json(self, response):
FILE: bbot/core/helpers/wordcloud.py
class WordCloud (line 15) | class WordCloud(dict):
method __init__ (line 79) | def __init__(self, parent_helper, *args, **kwargs):
method mutations (line 89) | def mutations(
method modifiers (line 137) | def modifiers(self, devops=True, cloud=True, letters=True, numbers=5, ...
method absorb_event (line 149) | def absorb_event(self, event):
method absorb_word (line 167) | def absorb_word(self, word, wordninja=True):
method add_word (line 191) | def add_word(self, word, lowercase=True):
method get_number_mutations (line 215) | def get_number_mutations(self, base, n=5, padding=2):
method truncate (line 286) | def truncate(self, limit):
method json (line 303) | def json(self, limit=None):
method default_filename (line 324) | def default_filename(self):
method save (line 327) | def save(self, filename=None, limit=None):
method load (line 368) | def load(self, filename=None):
class Mutator (line 405) | class Mutator(dict):
method mutations (line 411) | def mutations(self, words, max_mutations=None):
method mutate (line 421) | def mutate(self, word, max_mutations=None, mutations=None):
method top_mutations (line 433) | def top_mutations(self, n=None):
method _add_mutation (line 439) | def _add_mutation(self, mutation):
method add_word (line 448) | def add_word(self, word):
class DNSMutator (line 452) | class DNSMutator(Mutator):
method __init__ (line 484) | def __init__(self, *args, **kwargs):
method mutations (line 490) | def mutations(self, words, max_mutations=None):
method add_word (line 499) | def add_word(self, word):
FILE: bbot/core/helpers/yara_helper.py
class YaraHelper (line 4) | class YaraHelper:
method __init__ (line 5) | def __init__(self, parent_helper):
method compile_strings (line 8) | def compile_strings(self, strings: list[str], nocase=False):
method compile (line 35) | def compile(self, *args, **kwargs):
method match (line 38) | async def match(self, compiled_rules, text):
FILE: bbot/core/modules.py
class ModuleLoader (line 37) | class ModuleLoader:
method __init__ (line 53) | def __init__(self):
method copy (line 75) | def copy(self):
method preload_cache_file (line 81) | def preload_cache_file(self):
method module_dirs (line 85) | def module_dirs(self):
method add_module_dir (line 88) | def add_module_dir(self, module_dir):
method file_filter (line 104) | def file_filter(self, file):
method preload (line 111) | def preload(self, module_dirs=None):
method preload_cache (line 215) | def preload_cache(self):
method preload_cache (line 225) | def preload_cache(self, value):
method save_preload_cache (line 231) | def save_preload_cache(self):
method _preloaded (line 235) | def _preloaded(self):
method get_recursive_dirs (line 238) | def get_recursive_dirs(self, *dirs):
method preloaded (line 248) | def preloaded(self, type=None):
method configs (line 256) | def configs(self, type=None):
method find_and_replace (line 264) | def find_and_replace(self, **kwargs):
method check_type (line 268) | def check_type(self, module, type):
method preload_module (line 271) | def preload_module(self, module_file):
method load_modules (line 473) | def load_modules(self, module_names):
method load_module (line 485) | def load_module(self, module_name):
method check_dependency (line 531) | def check_dependency(self, event_type, modname, produced):
method add_or_create (line 539) | def add_or_create(d, k, *items):
method modules_table (line 545) | def modules_table(self, modules=None, mod_type=None, include_author=Fa...
method modules_options (line 601) | def modules_options(self, modules=None, mod_type=None):
method modules_options_table (line 617) | def modules_options_table(self, modules=None, mod_type=None):
method flags (line 624) | def flags(self, flags=None):
method flags_table (line 638) | def flags_table(self, flags=None):
method events (line 648) | def events(self):
method events_table (line 666) | def events_table(self):
method filter_modules (line 677) | def filter_modules(self, modules=None, mod_type=None):
method ensure_config_files (line 687) | def ensure_config_files(self):
FILE: bbot/core/multiprocess.py
class SharedInterpreterState (line 6) | class SharedInterpreterState:
method __init__ (line 13) | def __init__(self):
method is_main_process (line 19) | def is_main_process(self):
method is_scan_process (line 24) | def is_scan_process(self):
method main_pid (line 29) | def main_pid(self):
method scan_pid (line 38) | def scan_pid(self):
method update_scan_pid (line 46) | def update_scan_pid(self):
method cleanup (line 49) | def cleanup(self):
FILE: bbot/db/sql/models.py
function naive_datetime_validator (line 17) | def naive_datetime_validator(d: datetime):
class CustomJSONEncoder (line 30) | class CustomJSONEncoder(json.JSONEncoder):
method default (line 31) | def default(self, obj):
class BBOTBaseModel (line 38) | class BBOTBaseModel(SQLModel):
method __init__ (line 41) | def __init__(self, *args, **kwargs):
method validated (line 46) | def validated(self):
method to_json (line 54) | def to_json(self, **kwargs):
method _pk_column_names (line 58) | def _pk_column_names(cls):
method __hash__ (line 61) | def __hash__(self):
method __eq__ (line 64) | def __eq__(self, other):
class Event (line 71) | class Event(BBOTBaseModel, table=True):
method __init__ (line 72) | def __init__(self, *args, **kwargs):
method get_data (line 79) | def get_data(self):
method _get_data (line 83) | def _get_data(data, type):
class Scan (line 122) | class Scan(BBOTBaseModel, table=True):
class Target (line 137) | class Target(BBOTBaseModel, table=True):
FILE: bbot/errors.py
class BBOTError (line 1) | class BBOTError(Exception):
class ScanError (line 5) | class ScanError(BBOTError):
class ValidationError (line 9) | class ValidationError(BBOTError):
class ConfigLoadError (line 13) | class ConfigLoadError(BBOTError):
class HttpCompareError (line 17) | class HttpCompareError(BBOTError):
class DirectoryCreationError (line 21) | class DirectoryCreationError(BBOTError):
class DirectoryDeletionError (line 25) | class DirectoryDeletionError(BBOTError):
class NTLMError (line 29) | class NTLMError(BBOTError):
class InteractshError (line 33) | class InteractshError(BBOTError):
class WordlistError (line 37) | class WordlistError(BBOTError):
class CurlError (line 41) | class CurlError(BBOTError):
class PresetNotFoundError (line 45) | class PresetNotFoundError(BBOTError):
class EnableModuleError (line 49) | class EnableModuleError(BBOTError):
class EnableFlagError (line 53) | class EnableFlagError(BBOTError):
class BBOTArgumentError (line 57) | class BBOTArgumentError(BBOTError):
class PresetConditionError (line 61) | class PresetConditionError(BBOTError):
class PresetAbortError (line 65) | class PresetAbortError(PresetConditionError):
class BBOTEngineError (line 69) | class BBOTEngineError(BBOTError):
class WebError (line 73) | class WebError(BBOTEngineError):
class DNSError (line 77) | class DNSError(BBOTEngineError):
class ExcavateError (line 81) | class ExcavateError(BBOTError):
FILE: bbot/logger.py
function colorize (line 36) | def colorize(s, level="INFO"):
function log_to_stderr (line 42) | def log_to_stderr(msg, level="INFO", logname=True):
class GzipRotatingFileHandler (line 57) | class GzipRotatingFileHandler(logging.handlers.RotatingFileHandler):
method __init__ (line 63) | def __init__(self, *args, **kwargs):
method rotation_filename (line 68) | def rotation_filename(self, default_name):
method rotate (line 74) | def rotate(self, source, dest):
method emit (line 85) | def emit(self, record):
FILE: bbot/modules/ajaxpro.py
class ajaxpro (line 6) | class ajaxpro(BaseModule):
method handle_event (line 21) | async def handle_event(self, event):
method check_url_event (line 27) | async def check_url_event(self, event):
method check_http_response_event (line 38) | async def check_http_response_event(self, event):
method emit_technology (line 47) | async def emit_technology(self, event, detection_url):
method confirm_exploitability (line 61) | async def confirm_exploitability(self, detection_url, event):
FILE: bbot/modules/anubisdb.py
class anubisdb (line 4) | class anubisdb(subdomain_enum):
method request_url (line 21) | async def request_url(self, query):
method abort_if_pre (line 25) | def abort_if_pre(self, hostname):
method abort_if (line 35) | async def abort_if(self, event):
method parse_results (line 41) | async def parse_results(self, r, query):
FILE: bbot/modules/apkpure.py
class apkpure (line 6) | class apkpure(BaseModule):
method setup (line 20) | async def setup(self):
method filter_event (line 29) | async def filter_event(self, event):
method handle_event (line 35) | async def handle_event(self, event):
method download_apk (line 47) | async def download_apk(self, app_id):
FILE: bbot/modules/aspnet_bin_exposure.py
class aspnet_bin_exposure (line 4) | class aspnet_bin_exposure(BaseModule):
method normalize_url (line 24) | def normalize_url(url):
method _incoming_dedup_hash (line 27) | def _incoming_dedup_hash(self, event):
method handle_event (line 30) | async def handle_event(self, event):
method filter_event (line 77) | async def filter_event(self, event):
FILE: bbot/modules/azure_realm.py
class azure_realm (line 4) | class azure_realm(BaseModule):
method setup (line 14) | async def setup(self):
method handle_event (line 18) | async def handle_event(self, event):
method getuserrealm (line 34) | async def getuserrealm(self, domain):
FILE: bbot/modules/azure_tenant.py
class azure_tenant (line 4) | class azure_tenant(BaseModule):
method setup (line 18) | async def setup(self):
method handle_event (line 22) | async def handle_event(self, event):
method query (line 70) | async def query(self, domain):
FILE: bbot/modules/baddns.py
class baddns (line 9) | class baddns(BaseModule):
method select_modules (line 27) | def select_modules(self):
method set_modules (line 34) | def set_modules(self):
method setup (line 39) | async def setup(self):
method handle_event (line 57) | async def handle_event(self, event):
FILE: bbot/modules/baddns_direct.py
class baddns_direct (line 8) | class baddns_direct(BaseModule):
method setup (line 26) | async def setup(self):
method select_modules (line 35) | def select_modules(self):
method handle_event (line 42) | async def handle_event(self, event):
method filter_event (line 73) | async def filter_event(self, event):
FILE: bbot/modules/baddns_zone.py
class baddns_zone (line 4) | class baddns_zone(baddns_module):
method set_modules (line 21) | def set_modules(self):
method filter_event (line 25) | async def filter_event(self, event):
FILE: bbot/modules/badsecrets.py
class badsecrets (line 7) | class badsecrets(BaseModule):
method setup (line 22) | async def setup(self):
method _module_threads (line 36) | def _module_threads(self):
method handle_event (line 39) | async def handle_event(self, event):
FILE: bbot/modules/base.py
class BaseModule (line 13) | class BaseModule:
method __init__ (line 126) | def __init__(self, scan):
method setup (line 181) | async def setup(self):
method setup_deps (line 219) | async def setup_deps(self):
method handle_event (line 227) | async def handle_event(self, event, **kwargs):
method handle_batch (line 243) | async def handle_batch(self, *events):
method filter_event (line 259) | async def filter_event(self, event):
method finish (line 275) | async def finish(self):
method report (line 288) | async def report(self):
method cleanup (line 301) | async def cleanup(self):
method require_api_key (line 314) | async def require_api_key(self):
method api_key (line 343) | def api_key(self):
method api_key (line 348) | def api_key(self, api_keys):
method cycle_api_key (line 353) | def cycle_api_key(self):
method api_retries (line 361) | def api_retries(self):
method api_failure_abort_threshold (line 365) | def api_failure_abort_threshold(self):
method ping (line 368) | async def ping(self, url=None):
method batch_size (line 407) | def batch_size(self):
method module_threads (line 416) | def module_threads(self):
method event_handler_timeout (line 423) | def event_handler_timeout(self):
method auth_secret (line 430) | def auth_secret(self):
method event_received (line 441) | def event_received(self):
method get_watched_events (line 446) | def get_watched_events(self):
method _handle_batch (line 458) | async def _handle_batch(self):
method make_event (line 500) | def make_event(self, *args, **kwargs):
method update_event (line 540) | def update_event(self, event, **kwargs):
method emit_event (line 573) | async def emit_event(self, *args, **kwargs):
method _events_waiting (line 631) | async def _events_waiting(self, batch_size=None):
method num_incoming_events (line 673) | def num_incoming_events(self):
method start (line 679) | def start(self):
method _setup (line 690) | async def _setup(self, deps_only=False):
method _worker (line 717) | async def _worker(self):
method max_scope_distance (line 796) | def max_scope_distance(self):
method _event_precheck (line 806) | def _event_precheck(self, event):
method _event_postcheck (line 858) | async def _event_postcheck(self, event):
method _event_postcheck_inner (line 874) | async def _event_postcheck_inner(self, event):
method _scope_distance_check (line 919) | def _scope_distance_check(self, event):
method _cleanup (line 933) | async def _cleanup(self):
method run_task (line 942) | async def run_task(self, coro, name, n=1):
method _event_handler_watchdog (line 952) | async def _event_handler_watchdog(self):
method queue_event (line 972) | async def queue_event(self, event):
method queue_outgoing_event (line 1011) | async def queue_outgoing_event(self, event, **kwargs):
method set_error_state (line 1036) | def set_error_state(self, message=None, clear_outgoing_queue=False, cr...
method is_incoming_duplicate (line 1082) | def is_incoming_duplicate(self, event, add=False):
method _incoming_dedup_hash (line 1099) | def _incoming_dedup_hash(self, event):
method _outgoing_dedup_hash (line 1111) | def _outgoing_dedup_hash(self, event):
method get_per_host_hash (line 1120) | def get_per_host_hash(self, event):
method get_per_hostport_hash (line 1138) | def get_per_hostport_hash(self, event):
method get_per_domain_hash (line 1162) | def get_per_domain_hash(self, event):
method name (line 1182) | def name(self):
method helpers (line 1186) | def helpers(self):
method status (line 1190) | def status(self):
method running (line 1216) | def running(self):
method finished (line 1228) | def finished(self):
method run_process (line 1241) | async def run_process(self, *args, **kwargs):
method run_process_live (line 1245) | async def run_process_live(self, *args, **kwargs):
method prepare_api_request (line 1250) | def prepare_api_request(self, url, kwargs):
method api_request (line 1261) | async def api_request(self, *args, **kwargs):
method api_download (line 1314) | async def api_download(self, url, **kwargs):
method _get_retry_after (line 1332) | def _get_retry_after(self, r):
method _prepare_api_iter_req (line 1348) | def _prepare_api_iter_req(self, url, page, page_size, offset, **reques...
method _api_response_is_success (line 1355) | def _api_response_is_success(self, r):
method api_page_iter (line 1359) | async def api_page_iter(self, url, page_size=100, _json=True, next_key...
method preset (line 1423) | def preset(self):
method config (line 1427) | def config(self):
method incoming_event_queue (line 1442) | def incoming_event_queue(self):
method outgoing_event_queue (line 1451) | def outgoing_event_queue(self):
method priority (line 1457) | def priority(self):
method auth_required (line 1474) | def auth_required(self):
method http_timeout (line 1478) | def http_timeout(self):
method log (line 1485) | def log(self):
method memory_usage (line 1491) | def memory_usage(self):
method __str__ (line 1505) | def __str__(self):
method log_table (line 1508) | def log_table(self, *args, **kwargs):
method _is_graph_important (line 1541) | def _is_graph_important(self, event):
method preserve_graph (line 1545) | def preserve_graph(self):
method debug (line 1551) | def debug(self, *args, trace=False, **kwargs):
method verbose (line 1567) | def verbose(self, *args, trace=False, **kwargs):
method hugeverbose (line 1583) | def hugeverbose(self, *args, trace=False, **kwargs):
method info (line 1599) | def info(self, *args, trace=False, **kwargs):
method hugeinfo (line 1615) | def hugeinfo(self, *args, trace=False, **kwargs):
method success (line 1631) | def success(self, *args, trace=False, **kwargs):
method hugesuccess (line 1647) | def hugesuccess(self, *args, trace=False, **kwargs):
method warning (line 1663) | def warning(self, *args, trace=True, **kwargs):
method hugewarning (line 1679) | def hugewarning(self, *args, trace=True, **kwargs):
method error (line 1695) | def error(self, *args, trace=True, **kwargs):
method trace (line 1711) | def trace(self, msg=None):
method critical (line 1731) | def critical(self, *args, trace=True, **kwargs):
method help_text (line 1748) | def help_text(self):
class BaseInterceptModule (line 1790) | class BaseInterceptModule(BaseModule):
method _worker (line 1805) | async def _worker(self):
method get_incoming_event (line 1878) | async def get_incoming_event(self):
method forward_event (line 1884) | async def forward_event(self, event, kwargs):
method queue_outgoing_event (line 1890) | async def queue_outgoing_event(self, event, **kwargs):
method queue_event (line 1898) | async def queue_event(self, event, kwargs=None):
method _event_postcheck (line 1909) | async def _event_postcheck(self, event):
FILE: bbot/modules/bevigil.py
class bevigil (line 4) | class bevigil(subdomain_enum_apikey):
method setup (line 23) | async def setup(self):
method prepare_api_request (line 28) | def prepare_api_request(self, url, kwargs):
method handle_event (line 32) | async def handle_event(self, event):
method request_subdomains (line 55) | async def request_subdomains(self, query):
method request_urls (line 59) | async def request_urls(self, query):
method parse_subdomains (line 63) | async def parse_subdomains(self, r, query=None):
method parse_urls (line 70) | async def parse_urls(self, r, query=None):
FILE: bbot/modules/bucket_amazon.py
class bucket_amazon (line 4) | class bucket_amazon(bucket_template):
FILE: bbot/modules/bucket_digitalocean.py
class bucket_digitalocean (line 4) | class bucket_digitalocean(bucket_template):
method build_url (line 23) | def build_url(self, bucket_name, base_domain, region):
FILE: bbot/modules/bucket_file_enum.py
class bucket_file_enum (line 5) | class bucket_file_enum(BaseModule):
method setup (line 26) | async def setup(self):
method handle_event (line 30) | async def handle_event(self, event):
method handle_aws (line 35) | async def handle_aws(self, event):
FILE: bbot/modules/bucket_firebase.py
class bucket_firebase (line 4) | class bucket_firebase(bucket_template):
method filter_bucket (line 22) | def filter_bucket(self, event):
method build_url (line 28) | def build_url(self, bucket_name, base_domain, region):
method check_bucket_open (line 31) | async def check_bucket_open(self, bucket_name, url):
FILE: bbot/modules/bucket_google.py
class bucket_google (line 4) | class bucket_google(bucket_template):
method filter_bucket (line 38) | def filter_bucket(self, event):
method build_url (line 43) | def build_url(self, bucket_name, base_domain, region):
method check_bucket_open (line 46) | async def check_bucket_open(self, bucket_name, url):
method check_bucket_exists (line 63) | def check_bucket_exists(self, bucket_name, response):
FILE: bbot/modules/bucket_microsoft.py
class bucket_microsoft (line 4) | class bucket_microsoft(bucket_template):
method build_bucket_request (line 24) | def build_bucket_request(self, bucket_name, base_domain, region):
method check_bucket_exists (line 29) | def check_bucket_exists(self, bucket_name, response):
method clean_bucket_url (line 34) | def clean_bucket_url(self, url):
FILE: bbot/modules/bufferoverrun.py
class BufferOverrun (line 4) | class BufferOverrun(subdomain_enum_apikey):
method setup (line 20) | async def setup(self):
method prepare_api_request (line 24) | def prepare_api_request(self, url, kwargs):
method request_url (line 32) | async def request_url(self, query):
method parse_results (line 36) | async def parse_results(self, r, query):
FILE: bbot/modules/builtwith.py
class builtwith (line 16) | class builtwith(subdomain_enum_apikey):
method handle_event (line 30) | async def handle_event(self, event):
method request_domains (line 59) | async def request_domains(self, query):
method request_redirects (line 63) | async def request_redirects(self, query):
method parse_domains (line 67) | async def parse_domains(self, r, query):
method parse_redirects (line 97) | async def parse_redirects(self, r, query):
FILE: bbot/modules/bypass403.py
class bypass403 (line 78) | class bypass403(BaseModule):
method do_checks (line 85) | async def do_checks(self, compare_helper, event, collapse_threshold):
method handle_event (line 130) | async def handle_event(self, event):
method filter_event (line 162) | async def filter_event(self, event):
method format_signature (line 167) | def format_signature(self, sig, event):
FILE: bbot/modules/c99.py
class c99 (line 4) | class c99(subdomain_enum_apikey):
method ping (line 20) | async def ping(self):
method request_url (line 25) | async def request_url(self, query):
method parse_results (line 29) | async def parse_results(self, r, query):
FILE: bbot/modules/censys_dns.py
class censys_dns (line 4) | class censys_dns(censys):
method setup (line 25) | async def setup(self):
method query (line 29) | async def query(self, query):
FILE: bbot/modules/censys_ip.py
class censys_ip (line 4) | class censys_ip(censys):
method setup (line 34) | async def setup(self):
method filter_event (line 41) | async def filter_event(self, event):
method handle_event (line 48) | async def handle_event(self, event):
method _emit_host (line 163) | async def _emit_host(self, host, event, seen, source):
FILE: bbot/modules/certspotter.py
class certspotter (line 4) | class certspotter(subdomain_enum):
method request_url (line 16) | def request_url(self, query):
method parse_results (line 20) | async def parse_results(self, r, query):
FILE: bbot/modules/chaos.py
class chaos (line 4) | class chaos(subdomain_enum_apikey):
method prepare_api_request (line 20) | def prepare_api_request(self, url, kwargs):
method request_url (line 24) | async def request_url(self, query):
method parse_results (line 29) | async def parse_results(self, r, query):
FILE: bbot/modules/code_repository.py
class code_repository (line 5) | class code_repository(BaseModule):
method setup (line 27) | async def setup(self):
method handle_event (line 37) | async def handle_event(self, event):
FILE: bbot/modules/credshed.py
class credshed (line 6) | class credshed(subdomain_enum):
method setup (line 24) | async def setup(self):
method handle_event (line 45) | async def handle_event(self, event):
FILE: bbot/modules/crt.py
class crt (line 4) | class crt(subdomain_enum):
method setup (line 17) | async def setup(self):
method request_url (line 21) | async def request_url(self, query):
method parse_results (line 26) | async def parse_results(self, r, query):
FILE: bbot/modules/crt_db.py
class crt_db (line 7) | class crt_db(subdomain_enum):
method setup (line 25) | async def setup(self):
method request_url (line 29) | async def request_url(self, query):
method parse_results (line 59) | async def parse_results(self, results, query):
method cleanup (line 68) | async def cleanup(self):
FILE: bbot/modules/deadly/legba.py
function map_protocol_to_legba_plugin_name (line 14) | def map_protocol_to_legba_plugin_name(common_protocol_name: str) -> str:
class legba (line 18) | class legba(BaseModule):
method setup (line 70) | async def setup(self):
method filter_event (line 78) | async def filter_event(self, event):
method handle_event (line 87) | async def handle_event(self, event):
method parse_output (line 105) | async def parse_output(self, output_filepath, event):
method construct_command (line 150) | async def construct_command(self, host, port, protocol):
FILE: bbot/modules/dehashed.py
class dehashed (line 6) | class dehashed(subdomain_enum):
method setup (line 22) | async def setup(self):
method handle_event (line 36) | async def handle_event(self, event):
method query (line 90) | async def query(self, domain):
FILE: bbot/modules/digitorus.py
class digitorus (line 6) | class digitorus(subdomain_enum):
method request_url (line 18) | async def request_url(self, query):
method parse_results (line 22) | async def parse_results(self, r, query):
FILE: bbot/modules/dnsbimi.py
class dnsbimi (line 46) | class dnsbimi(BaseModule):
method setup (line 66) | async def setup(self):
method _incoming_dedup_hash (line 73) | def _incoming_dedup_hash(self, event):
method filter_event (line 78) | async def filter_event(self, event):
method inspectBIMI (line 88) | async def inspectBIMI(self, event, domain):
method handle_event (line 141) | async def handle_event(self, event):
FILE: bbot/modules/dnsbrute.py
class dnsbrute (line 4) | class dnsbrute(subdomain_enum):
method setup_deps (line 26) | async def setup_deps(self):
method setup (line 32) | async def setup(self):
method filter_event (line 38) | async def filter_event(self, event):
method handle_event (line 55) | async def handle_event(self, event):
FILE: bbot/modules/dnsbrute_mutations.py
class dnsbrute_mutations (line 6) | class dnsbrute_mutations(BaseModule):
method setup (line 24) | async def setup(self):
method handle_event (line 33) | async def handle_event(self, event):
method get_parent_event (line 45) | async def get_parent_event(self, subdomain):
method finish (line 52) | async def finish(self):
method abort_if (line 150) | def abort_if(self, event):
FILE: bbot/modules/dnscaa.py
class dnscaa (line 41) | class dnscaa(BaseModule):
method setup (line 61) | async def setup(self):
method filter_event (line 68) | async def filter_event(self, event):
method handle_event (line 78) | async def handle_event(self, event):
FILE: bbot/modules/dnscommonsrv.py
class dnscommonsrv (line 5) | class dnscommonsrv(subdomain_enum):
method setup (line 16) | async def setup(self):
method filter_event (line 21) | async def filter_event(self, event):
method handle_event (line 27) | async def handle_event(self, event):
FILE: bbot/modules/dnsdumpster.py
class dnsdumpster (line 6) | class dnsdumpster(subdomain_enum):
method setup (line 18) | async def setup(self):
method query (line 22) | async def query(self, domain):
FILE: bbot/modules/dnstlsrpt.py
class dnstlsrpt (line 34) | class dnstlsrpt(BaseModule):
method setup (line 54) | async def setup(self):
method _incoming_dedup_hash (line 60) | def _incoming_dedup_hash(self, event):
method filter_event (line 65) | async def filter_event(self, event):
method handle_event (line 75) | async def handle_event(self, event):
FILE: bbot/modules/docker_pull.py
class docker_pull (line 8) | class docker_pull(BaseModule):
method setup (line 25) | async def setup(self):
method filter_event (line 45) | async def filter_event(self, event):
method handle_event (line 51) | async def handle_event(self, event):
method get_registry_and_repository (line 67) | def get_registry_and_repository(self, repository_url):
method docker_api_request (line 77) | async def docker_api_request(self, url: str):
method get_tags (line 101) | async def get_tags(self, registry, repository):
method get_manifest (line 122) | async def get_manifest(self, registry, repository, tag):
method get_layers (line 136) | async def get_layers(self, manifest):
method download_blob (line 145) | async def download_blob(self, registry, repository, digest):
method create_local_manifest (line 153) | async def create_local_manifest(self, config, repository, tag, layers):
method download_and_get_filename (line 157) | async def download_and_get_filename(self, registry, repository, digest):
method write_file_to_tar (line 166) | async def write_file_to_tar(self, tar, filename, file_content):
method download_docker_repo (line 174) | async def download_docker_repo(self, repository_url):
method download_and_write_to_tar (line 182) | async def download_and_write_to_tar(self, registry, repository, tag):
FILE: bbot/modules/dockerhub.py
class dockerhub (line 4) | class dockerhub(BaseModule):
method filter_event (line 19) | async def filter_event(self, event):
method handle_event (line 25) | async def handle_event(self, event):
method handle_org_stub (line 31) | async def handle_org_stub(self, event):
method handle_social (line 49) | async def handle_social(self, event):
method get_repos (line 64) | async def get_repos(self, username):
FILE: bbot/modules/dotnetnuke.py
class dotnetnuke (line 5) | class dotnetnuke(BaseModule):
method setup (line 29) | async def setup(self):
method interactsh_callback (line 43) | async def interactsh_callback(self, r):
method handle_event (line 67) | async def handle_event(self, event):
method cleanup (line 191) | async def cleanup(self):
method finish (line 201) | async def finish(self):
FILE: bbot/modules/emailformat.py
class emailformat (line 4) | class emailformat(BaseModule):
method setup (line 18) | async def setup(self):
method handle_event (line 22) | async def handle_event(self, event):
FILE: bbot/modules/extractous.py
class extractous (line 6) | class extractous(BaseModule):
method setup (line 71) | async def setup(self):
method filter_event (line 75) | async def filter_event(self, event):
method handle_event (line 83) | async def handle_event(self, event):
function extract_text (line 102) | def extract_text(file_path):
FILE: bbot/modules/ffuf.py
class ffuf (line 9) | class ffuf(BaseModule):
method setup_deps (line 40) | async def setup_deps(self):
method setup (line 44) | async def setup(self):
method handle_event (line 61) | async def handle_event(self, event):
method filter_event (line 89) | async def filter_event(self, event):
method baseline_ffuf (line 95) | async def baseline_ffuf(self, url, exts=[""], prefix="", suffix="", mo...
method execute_ffuf (line 202) | async def execute_ffuf(
method generate_templist (line 328) | def generate_templist(self, prefix=None):
method generate_wordlist (line 342) | def generate_wordlist(self, wordlist_file):
FILE: bbot/modules/ffuf_shortnames.py
class ffuf_shortnames (line 9) | class ffuf_shortnames(ffuf):
method generate_templist (line 51) | def generate_templist(self, hint, shortname_type):
method predict (line 62) | def predict(self, prefix, n=25, model="endpoint"):
method find_common_prefixes (line 68) | def find_common_prefixes(strings, minimum_set_length=4):
method setup_deps (line 90) | async def setup_deps(self):
method setup (line 98) | async def setup(self):
method build_extension_list (line 167) | def build_extension_list(self, event):
method find_delimiter (line 180) | def find_delimiter(self, hint):
method filter_event (line 188) | async def filter_event(self, event):
method find_subword (line 195) | def find_subword(self, word):
method handle_event (line 203) | async def handle_event(self, event):
method finish (line 321) | async def finish(self):
FILE: bbot/modules/filedownload.py
class filedownload (line 7) | class filedownload(BaseModule):
method setup_deps (line 97) | async def setup_deps(self):
method setup (line 103) | async def setup(self):
method filter_event (line 122) | async def filter_event(self, event):
method hash_event (line 133) | def hash_event(self, event):
method handle_event (line 138) | async def handle_event(self, event):
method download_file (line 152) | async def download_file(self, url, content_type=None, source_event=None):
method make_filename (line 168) | def make_filename(self, url, content_type=None):
method report (line 200) | async def report(self):
FILE: bbot/modules/fingerprintx.py
class fingerprintx (line 6) | class fingerprintx(BaseModule):
method setup (line 54) | async def setup(self):
method filter_event (line 58) | async def filter_event(self, event):
method handle_batch (line 65) | async def handle_batch(self, *events):
FILE: bbot/modules/fullhunt.py
class fullhunt (line 4) | class fullhunt(subdomain_enum_apikey):
method setup (line 19) | async def setup(self):
method ping (line 23) | async def ping(self):
method prepare_api_request (line 29) | def prepare_api_request(self, url, kwargs):
method request_url (line 33) | async def request_url(self, query):
method parse_results (line 38) | async def parse_results(self, r, query):
FILE: bbot/modules/generic_ssrf.py
class BaseSubmodule (line 37) | class BaseSubmodule:
method __init__ (line 42) | def __init__(self, generic_ssrf):
method set_base_url (line 46) | def set_base_url(self, event):
method create_paths (line 49) | def create_paths(self):
method test (line 52) | async def test(self, event):
method process (line 66) | def process(self, event, r, subdomain_tag):
class Generic_SSRF (line 81) | class Generic_SSRF(BaseSubmodule):
method set_base_url (line 85) | def set_base_url(self, event):
method create_paths (line 88) | def create_paths(self):
class Generic_SSRF_POST (line 100) | class Generic_SSRF_POST(BaseSubmodule):
method set_base_url (line 104) | def set_base_url(self, event):
method test (line 107) | async def test(self, event):
class Generic_XXE (line 129) | class Generic_XXE(BaseSubmodule):
method test (line 134) | async def test(self, event):
class generic_ssrf (line 152) | class generic_ssrf(BaseModule):
method setup (line 167) | async def setup(self):
method handle_event (line 195) | async def handle_event(self, event):
method interactsh_callback (line 199) | async def interactsh_callback(self, r):
method cleanup (line 245) | async def cleanup(self):
method finish (line 255) | async def finish(self):
FILE: bbot/modules/git.py
class git (line 6) | class git(BaseModule):
method handle_event (line 20) | async def handle_event(self, event):
FILE: bbot/modules/git_clone.py
class git_clone (line 6) | class git_clone(github):
method setup (line 25) | async def setup(self):
method filter_event (line 31) | async def filter_event(self, event):
method handle_event (line 36) | async def handle_event(self, event):
method clone_git_repository (line 47) | async def clone_git_repository(self, repository_url):
FILE: bbot/modules/gitdumper.py
class gitdumper (line 7) | class gitdumper(BaseModule):
method setup (line 29) | async def setup(self):
method filter_event (line 110) | async def filter_event(self, event):
method handle_event (line 116) | async def handle_event(self, event):
method directory_listing_enabled (line 141) | async def directory_listing_enabled(self, repo_url):
method recursive_dir_list (line 148) | async def recursive_dir_list(self, dir_listing):
method git_fuzz (line 169) | async def git_fuzz(self, repo_url, repo_folder):
method download_current_branch (line 186) | async def download_current_branch(self, repo_url, repo_folder):
method download_git_objects (line 192) | async def download_git_objects(self, url, folder):
method download_git_packs (line 196) | async def download_git_packs(self, url, folder):
method regex_files (line 204) | async def regex_files(self, regex, folder=Path(), file=Path(), files=[]):
method regex_file (line 218) | async def regex_file(self, regex, file=Path()):
method download_object (line 227) | async def download_object(self, object, repo_url, repo_folder):
method download_files (line 235) | async def download_files(self, urls, folder):
method git_catfile (line 251) | async def git_catfile(self, hash, option="-t", folder=Path()):
method git_checkout (line 260) | async def git_checkout(self, folder):
FILE: bbot/modules/github_codesearch.py
class github_codesearch (line 5) | class github_codesearch(github, subdomain_enum):
method setup (line 20) | async def setup(self):
method handle_event (line 24) | async def handle_event(self, event):
method query (line 42) | async def query(self, query):
method raw_url (line 84) | def raw_url(self, url):
FILE: bbot/modules/github_org.py
class github_org (line 4) | class github_org(github):
method setup (line 22) | async def setup(self):
method _api_response_is_success (line 27) | def _api_response_is_success(self, r):
method filter_event (line 31) | async def filter_event(self, event):
method handle_event (line 41) | async def handle_event(self, event):
method query_org_repos (line 108) | async def query_org_repos(self, query):
method query_org_members (line 136) | async def query_org_members(self, query):
method query_user_repos (line 164) | async def query_user_repos(self, query):
method validate_org (line 192) | async def validate_org(self, org):
FILE: bbot/modules/github_usersearch.py
class github_usersearch (line 5) | class github_usersearch(github, subdomain_enum):
method handle_event (line 18) | async def handle_event(self, event):
method query_users (line 39) | async def query_users(self, query):
FILE: bbot/modules/github_workflows.py
class github_workflows (line 8) | class github_workflows(github):
method setup (line 27) | async def setup(self):
method _api_response_is_success (line 40) | def _api_response_is_success(self, r):
method filter_event (line 44) | async def filter_event(self, event):
method handle_event (line 51) | async def handle_event(self, event):
method get_workflows (line 100) | async def get_workflows(self, owner, repo):
method get_workflow_runs (line 127) | async def get_workflow_runs(self, owner, repo, workflow_id):
method download_run_logs (line 150) | async def download_run_logs(self, owner, repo, run_id):
method get_run_artifacts (line 188) | async def get_run_artifacts(self, owner, repo, run_id):
method download_run_artifacts (line 211) | async def download_run_artifacts(self, owner, repo, artifact_id, artif...
FILE: bbot/modules/gitlab_com.py
class gitlab_com (line 4) | class gitlab_com(GitLabBaseModule):
method handle_event (line 22) | async def handle_event(self, event):
method filter_event (line 25) | async def filter_event(self, event):
FILE: bbot/modules/gitlab_onprem.py
class gitlab_onprem (line 4) | class gitlab_onprem(GitLabBaseModule):
method handle_event (line 28) | async def handle_event(self, event):
method filter_event (line 36) | async def filter_event(self, event):
method handle_http_response (line 54) | async def handle_http_response(self, event):
method handle_technology (line 73) | async def handle_technology(self, event):
FILE: bbot/modules/google_playstore.py
class google_playstore (line 4) | class google_playstore(BaseModule):
method setup (line 16) | async def setup(self):
method filter_event (line 20) | async def filter_event(self, event):
method handle_event (line 26) | async def handle_event(self, event):
method handle_url (line 32) | async def handle_url(self, event):
method handle_org_stub (line 43) | async def handle_org_stub(self, event):
method query (line 60) | async def query(self, query):
method validate_apk (line 76) | async def validate_apk(self, apk_name):
FILE: bbot/modules/gowitness.py
class gowitness (line 13) | class gowitness(BaseModule):
method setup (line 56) | async def setup(self):
method prep (line 130) | def prep(self):
method filter_event (line 139) | async def filter_event(self, event):
method handle_batch (line 155) | async def handle_batch(self, *events):
method construct_command (line 226) | def construct_command(self):
method get_new_screenshots (line 253) | async def get_new_screenshots(self):
method get_new_network_logs (line 268) | async def get_new_network_logs(self):
method get_new_technologies (line 282) | async def get_new_technologies(self):
method cur_execute (line 296) | async def cur_execute(self, cur, query):
method report (line 303) | async def report(self):
FILE: bbot/modules/graphql_introspection.py
class graphql_introspection (line 6) | class graphql_introspection(BaseModule):
method setup (line 24) | async def setup(self):
method filter_event (line 32) | async def filter_event(self, event):
method handle_event (line 37) | async def handle_event(self, event):
FILE: bbot/modules/hackertarget.py
class hackertarget (line 4) | class hackertarget(subdomain_enum):
method request_url (line 16) | async def request_url(self, query):
method parse_results (line 21) | async def parse_results(self, r, query):
FILE: bbot/modules/host_header.py
class host_header (line 5) | class host_header(BaseModule):
method setup (line 20) | async def setup(self):
method rand_string (line 34) | def rand_string(self, *args, **kwargs):
method interactsh_callback (line 37) | async def interactsh_callback(self, r):
method finish (line 62) | async def finish(self):
method cleanup (line 71) | async def cleanup(self):
method handle_event (line 81) | async def handle_event(self, event):
FILE: bbot/modules/httpx.py
class httpx (line 11) | class httpx(BaseModule):
method setup (line 56) | async def setup(self):
method filter_event (line 64) | async def filter_event(self, event):
method make_url_metadata (line 86) | def make_url_metadata(self, event):
method _incoming_dedup_hash (line 101) | def _incoming_dedup_hash(self, event):
method handle_batch (line 105) | async def handle_batch(self, *events):
method cleanup (line 226) | async def cleanup(self):
FILE: bbot/modules/hunt.py
class hunt (line 282) | class hunt(BaseModule):
method handle_event (line 292) | async def handle_event(self, event):
FILE: bbot/modules/hunterio.py
class hunterio (line 4) | class hunterio(subdomain_enum_apikey):
method handle_event (line 21) | async def handle_event(self, event):
method query (line 51) | async def query(self, query):
FILE: bbot/modules/iis_shortnames.py
function encode_all (line 8) | def encode_all(string):
class IISShortnamesError (line 12) | class IISShortnamesError(Exception):
class iis_shortnames (line 16) | class iis_shortnames(BaseModule):
method detect (line 35) | async def detect(self, target):
method setup (line 70) | async def setup(self):
method normalize_url (line 75) | def normalize_url(url):
method directory_confirm (line 78) | async def directory_confirm(self, target, method, url_hint, affirmativ...
method duplicate_check (line 89) | async def duplicate_check(self, target, method, url_hint, affirmative_...
method solve_valid_chars (line 119) | async def solve_valid_chars(self, method, target, affirmative_status_c...
method solve_shortname_recursive (line 145) | async def solve_shortname_recursive(
method handle_event (line 218) | async def handle_event(self, event):
method filter_event (line 360) | async def filter_event(self, event):
FILE: bbot/modules/internal/aggregate.py
class aggregate (line 4) | class aggregate(BaseReportModule):
method report (line 13) | async def report(self):
FILE: bbot/modules/internal/base.py
class BaseInternalModule (line 6) | class BaseInternalModule(BaseModule):
method log (line 13) | def log(self):
FILE: bbot/modules/internal/cloudcheck.py
class CloudCheck (line 8) | class CloudCheck(BaseInterceptModule):
method setup (line 19) | async def setup(self):
method filter_event (line 26) | async def filter_event(self, event):
method handle_event (line 31) | async def handle_event(self, event, **kwargs):
method cloud_hostname_regexes (line 90) | async def cloud_hostname_regexes(self):
FILE: bbot/modules/internal/dnsresolve.py
class DNSResolve (line 10) | class DNSResolve(BaseInterceptModule):
class HostModule (line 17) | class HostModule(BaseModule):
method module_threads (line 22) | def module_threads(self):
method setup (line 25) | async def setup(self):
method filter_event (line 47) | async def filter_event(self, event):
method handle_event (line 52) | async def handle_event(self, event, **kwargs):
method handle_wildcard_event (line 136) | async def handle_wildcard_event(self, event):
method emit_dns_children (line 175) | async def emit_dns_children(self, event):
method emit_dns_children_raw (line 203) | async def emit_dns_children_raw(self, event, dns_tags):
method check_scope (line 221) | def check_scope(self, event):
method resolve_event (line 248) | async def resolve_event(self, event, types):
method get_dns_parent (line 287) | def get_dns_parent(self, event):
method emit_raw_records (line 315) | def emit_raw_records(self):
method _dns_search_distance (line 324) | def _dns_search_distance(self):
method _make_dummy_module (line 327) | def _make_dummy_module(self, name):
FILE: bbot/modules/internal/excavate.py
function find_subclasses (line 15) | def find_subclasses(obj, base_class):
function _exclude_key (line 43) | def _exclude_key(original_dict, key_to_exclude):
function extract_params_url (line 65) | def extract_params_url(parsed_url):
function extract_params_location (line 84) | def extract_params_location(location_header_value, original_parsed_url):
class YaraRuleSettings (line 107) | class YaraRuleSettings:
method __init__ (line 108) | def __init__(self, description, tags, emit_match):
class ExcavateRule (line 114) | class ExcavateRule:
method __init__ (line 127) | def __init__(self, excavate):
method preprocess (line 132) | async def preprocess(self, r, event, discovery_context):
method process (line 172) | async def process(self, yara_results, event, yara_rule_settings, disco...
method report_prep (line 202) | async def report_prep(self, event_data, event_type, event, tags):
method report (line 230) | async def report(
class CustomExtractor (line 275) | class CustomExtractor(ExcavateRule):
method __init__ (line 278) | def __init__(self, excavate):
method process (line 281) | async def process(self, yara_results, event, yara_rule_settings, disco...
class excavate (line 296) | class excavate(BaseInternalModule, BaseInterceptModule):
method in_bl (line 334) | def in_bl(self, value):
method url_unparse (line 347) | def url_unparse(self, param_type, parsed_url):
class ParameterExtractor (line 365) | class ParameterExtractor(ExcavateRule):
class ParameterExtractorRule (line 369) | class ParameterExtractorRule:
method extract (line 372) | async def extract(self):
method __init__ (line 375) | def __init__(self, excavate, result):
class GetJquery (line 379) | class GetJquery(ParameterExtractorRule):
method extract (line 385) | async def extract(self):
method convert_to_dict (line 399) | async def convert_to_dict(self, extracted_str):
class PostJquery (line 411) | class PostJquery(GetJquery):
class HtmlTags (line 417) | class HtmlTags(ParameterExtractorRule):
method extract (line 423) | async def extract(self):
class AjaxJquery (line 438) | class AjaxJquery(ParameterExtractorRule):
method extract (line 450) | async def extract(self):
class GetForm (line 493) | class GetForm(ParameterExtractorRule):
method extract (line 510) | async def extract(self):
class GetForm2 (line 547) | class GetForm2(GetForm):
class PostForm (line 550) | class PostForm(GetForm):
class PostForm2 (line 556) | class PostForm2(PostForm):
class PostForm_NoAction (line 559) | class PostForm_NoAction(PostForm):
class _GenericForm (line 564) | class _GenericForm(GetForm):
method __init__ (line 571) | def __init__(self, excavate):
method process (line 585) | async def process(self, yara_results, event, yara_rule_settings, dis...
class CSPExtractor (line 653) | class CSPExtractor(ExcavateRule):
method process (line 660) | async def process(self, yara_results, event, yara_rule_settings, dis...
class EmailExtractor (line 667) | class EmailExtractor(ExcavateRule):
method process (line 674) | async def process(self, yara_results, event, yara_rule_settings, dis...
class JWTExtractor (line 682) | class JWTExtractor(ExcavateRule):
class ErrorExtractor (line 688) | class ErrorExtractor(ExcavateRule):
method __init__ (line 708) | def __init__(self, excavate):
method process (line 718) | async def process(self, yara_results, event, yara_rule_settings, dis...
class SerializationExtractor (line 726) | class SerializationExtractor(ExcavateRule):
method __init__ (line 739) | def __init__(self, excavate):
method process (line 749) | async def process(self, yara_results, event, yara_rule_settings, dis...
class FunctionalityExtractor (line 757) | class FunctionalityExtractor(ExcavateRule):
class NonHttpSchemeExtractor (line 764) | class NonHttpSchemeExtractor(ExcavateRule):
method process (line 772) | async def process(self, yara_results, event, yara_rule_settings, dis...
class URLExtractor (line 813) | class URLExtractor(ExcavateRule):
method process (line 847) | async def process(self, yara_results, event, yara_rule_settings, dis...
method report_prep (line 898) | async def report_prep(self, event_data, event_type, event, tags, **k...
class HostnameExtractor (line 911) | class HostnameExtractor(ExcavateRule):
method __init__ (line 916) | def __init__(self, excavate):
method process (line 920) | async def process(self, yara_results, event, yara_rule_settings, dis...
class LoginPageExtractor (line 925) | class LoginPageExtractor(ExcavateRule):
method process (line 941) | async def process(self, yara_results, event, yara_rule_settings, dis...
method add_yara_rule (line 945) | def add_yara_rule(self, rule_name, rule_content, rule_instance):
method extract_yara_rules (line 950) | async def extract_yara_rules(self, rules_content):
method emit_web_parameter (line 954) | async def emit_web_parameter(
method emit_custom_parameters (line 968) | async def emit_custom_parameters(self, event, config_key, param_type, ...
method setup (line 984) | async def setup(self):
method search (line 1073) | async def search(self, data, event, content_type, discovery_context="H...
method handle_event (line 1131) | async def handle_event(self, event, **kwargs):
method help_text (line 1282) | def help_text(self):
FILE: bbot/modules/internal/speculate.py
class speculate (line 8) | class speculate(BaseInternalModule):
method setup (line 46) | async def setup(self):
method handle_event (line 79) | async def handle_event(self, event):
FILE: bbot/modules/internal/unarchive.py
class unarchive (line 7) | class unarchive(BaseInternalModule):
method setup (line 17) | async def setup(self):
method filter_event (line 31) | async def filter_event(self, event):
method handle_event (line 48) | async def handle_event(self, event):
method extract_file (line 69) | async def extract_file(self, path, output_dir):
FILE: bbot/modules/ip2location.py
class IP2Location (line 4) | class IP2Location(BaseModule):
method setup (line 29) | async def setup(self):
method ping (line 33) | async def ping(self):
method build_url (line 37) | def build_url(self, data):
method handle_event (line 43) | async def handle_event(self, event):
FILE: bbot/modules/ipneighbor.py
class ipneighbor (line 6) | class ipneighbor(BaseModule):
method setup (line 19) | async def setup(self):
method filter_event (line 24) | async def filter_event(self, event):
method handle_event (line 29) | async def handle_event(self, event):
FILE: bbot/modules/ipstack.py
class Ipstack (line 4) | class Ipstack(BaseModule):
method setup (line 28) | async def setup(self):
method handle_event (line 31) | async def handle_event(self, event):
FILE: bbot/modules/jadx.py
class jadx (line 6) | class jadx(BaseModule):
method setup (line 40) | async def setup(self):
method filter_event (line 44) | async def filter_event(self, event):
method handle_event (line 52) | async def handle_event(self, event):
method decompile_apk (line 70) | async def decompile_apk(self, path, output_dir):
FILE: bbot/modules/leakix.py
class leakix (line 4) | class leakix(subdomain_enum_apikey):
method setup (line 20) | async def setup(self):
method prepare_api_request (line 27) | def prepare_api_request(self, url, kwargs):
method request_url (line 33) | async def request_url(self, query):
method parse_results (line 38) | async def parse_results(self, r, query=None):
FILE: bbot/modules/lightfuzz/lightfuzz.py
class lightfuzz (line 7) | class lightfuzz(BaseModule):
method setup (line 39) | async def setup(self):
method interactsh_callback (line 76) | async def interactsh_callback(self, r):
method _outgoing_dedup_hash (line 98) | def _outgoing_dedup_hash(self, event):
method run_submodule (line 110) | async def run_submodule(self, submodule, event):
method handle_event (line 131) | async def handle_event(self, event):
method cleanup (line 180) | async def cleanup(self):
method finish (line 190) | async def finish(self):
method filter_event (line 199) | async def filter_event(self, event):
method help_text (line 215) | def help_text(self):
FILE: bbot/modules/lightfuzz/submodules/base.py
class BaseLightfuzz (line 7) | class BaseLightfuzz:
method __init__ (line 11) | def __init__(self, lightfuzz, event):
method is_hex (line 18) | def is_hex(s):
method is_base64 (line 26) | def is_base64(s):
method additional_params_process (line 35) | def additional_params_process(self, additional_params, additional_para...
method conditional_urlencode (line 57) | def conditional_urlencode(self, probe, event_type, skip_urlencoding=Fa...
method build_query_string (line 66) | def build_query_string(self, probe, parameter_name, additional_params=...
method prepare_request (line 73) | def prepare_request(
method compare_baseline (line 123) | def compare_baseline(
method baseline_probe (line 158) | async def baseline_probe(self, cookies):
method compare_probe (line 176) | async def compare_probe(
method standard_probe (line 216) | async def standard_probe(
method conversion_note (line 241) | def conversion_note(self):
method metadata (line 248) | def metadata(self):
method incoming_probe_value (line 256) | def incoming_probe_value(self, populate_empty=True):
method outgoing_probe_value (line 273) | def outgoing_probe_value(self, outgoing_probe_value):
method get_submodule_name (line 290) | def get_submodule_name(self):
method log (line 294) | def log(self, level, message, *args, **kwargs):
method debug (line 300) | def debug(self, message, *args, **kwargs):
method verbose (line 303) | def verbose(self, message, *args, **kwargs):
method info (line 306) | def info(self, message, *args, **kwargs):
method hugeinfo (line 309) | def hugeinfo(self, message, *args, **kwargs):
method warning (line 312) | def warning(self, message, *args, **kwargs):
method hugewarning (line 315) | def hugewarning(self, message, *args, **kwargs):
method error (line 318) | def error(self, message, *args, **kwargs):
method critical (line 321) | def critical(self, message, *args, **kwargs):
FILE: bbot/modules/lightfuzz/submodules/cmdi.py
class cmdi (line 7) | class cmdi(BaseLightfuzz):
method fuzz (line 26) | async def fuzz(self):
FILE: bbot/modules/lightfuzz/submodules/crypto.py
class crypto (line 12) | class crypto(BaseLightfuzz):
method is_hex (line 38) | def is_hex(s):
method is_base64 (line 46) | def is_base64(s):
method compiled_rules (line 77) | def compiled_rules(self):
method format_agnostic_decode (line 87) | def format_agnostic_decode(input_string, urldecode=False):
method format_agnostic_encode (line 112) | def format_agnostic_encode(data, encoding, urlencode=False):
method modify_string (line 138) | def modify_string(input_string, action="truncate", position=None, exte...
method is_likely_encrypted (line 183) | def is_likely_encrypted(self, data, threshold=4.5):
method cryptanalysis (line 188) | def cryptanalysis(self, input_string):
method possible_block_sizes (line 200) | def possible_block_sizes(ciphertext_length):
method padding_oracle_execute (line 209) | async def padding_oracle_execute(self, original_data, encoding, block_...
method padding_oracle (line 287) | async def padding_oracle(self, probe_value, cookies):
method error_string_search (line 313) | async def error_string_search(self, text_dict, baseline_text):
method identify_hash_function (line 354) | def identify_hash_function(hash_bytes):
method fuzz (line 367) | async def fuzz(self):
FILE: bbot/modules/lightfuzz/submodules/esi.py
class esi (line 4) | class esi(BaseLightfuzz):
method check_probe (line 16) | async def check_probe(self, cookies, probe, match):
method fuzz (line 31) | async def fuzz(self):
FILE: bbot/modules/lightfuzz/submodules/path.py
class path (line 7) | class path(BaseLightfuzz):
method fuzz (line 28) | async def fuzz(self):
FILE: bbot/modules/lightfuzz/submodules/serial.py
class serial (line 5) | class serial(BaseLightfuzz):
method is_possibly_serialized (line 56) | def is_possibly_serialized(self, value):
method fuzz (line 81) | async def fuzz(self):
FILE: bbot/modules/lightfuzz/submodules/sqli.py
class sqli (line 7) | class sqli(BaseLightfuzz):
method evaluate_delay (line 41) | def evaluate_delay(self, mean_baseline, measured_delay):
method fuzz (line 72) | async def fuzz(self):
FILE: bbot/modules/lightfuzz/submodules/ssti.py
class ssti (line 4) | class ssti(BaseLightfuzz):
method fuzz (line 16) | async def fuzz(self):
FILE: bbot/modules/lightfuzz/submodules/xss.py
class xss (line 6) | class xss(BaseLightfuzz):
method determine_context (line 26) | async def determine_context(self, cookies, html, random_string):
method determine_javascript_quote_context (line 61) | async def determine_javascript_quote_context(self, target, text):
method check_probe (line 87) | async def check_probe(self, cookies, probe, match, context):
method fuzz (line 100) | async def fuzz(self):
FILE: bbot/modules/medusa.py
class medusa (line 5) | class medusa(BaseModule):
method setup_deps (line 104) | async def setup_deps(self):
method setup (line 108) | async def setup(self):
method filter_event (line 114) | async def filter_event(self, event):
method handle_event (line 123) | async def handle_event(self, event):
method parse_output (line 148) | async def parse_output(self, output, protocol_version):
method construct_command (line 168) | async def construct_command(self, host, port, protocol, protocol_versi...
method create_vuln_event (line 219) | def create_vuln_event(self, severity, description, source_event):
FILE: bbot/modules/myssl.py
class myssl (line 4) | class myssl(subdomain_enum):
method request_url (line 16) | async def request_url(self, query):
method parse_results (line 20) | async def parse_results(self, r, query):
FILE: bbot/modules/newsletters.py
class newsletters (line 18) | class newsletters(BaseModule):
method find_type (line 30) | def find_type(self, soup):
method handle_event (line 38) | async def handle_event(self, event):
FILE: bbot/modules/ntlm.py
class ntlm (line 62) | class ntlm(BaseModule):
method setup (line 82) | async def setup(self):
method handle_event (line 87) | async def handle_event(self, event):
method filter_event (line 136) | async def filter_event(self, event):
FILE: bbot/modules/nuclei.py
class nuclei (line 7) | class nuclei(BaseModule):
method setup (line 64) | async def setup(self):
method handle_batch (line 143) | async def handle_batch(self, *events):
method correlate_event (line 196) | def correlate_event(self, events, host):
method execute_nuclei (line 204) | async def execute_nuclei(self, nuclei_input):
method log_nuclei_status (line 287) | def log_nuclei_status(self, line):
method cleanup (line 307) | async def cleanup(self):
method filter_event (line 311) | async def filter_event(self, event):
class NucleiBudget (line 321) | class NucleiBudget:
method __init__ (line 322) | def __init__(self, nuclei_module):
method get_yaml_list (line 330) | def get_yaml_list(self):
method find_budget_paths (line 334) | def find_budget_paths(self, budget):
method get_yaml_request_attr (line 348) | def get_yaml_request_attr(self, yf, attr):
method get_yaml_info_attr (line 358) | def get_yaml_info_attr(self, yf, attr):
method find_collapsible_templates (line 366) | def find_collapsible_templates(self):
method parse_yaml (line 409) | def parse_yaml(self, yamlfile):
FILE: bbot/modules/oauth.py
class OAUTH (line 6) | class OAUTH(BaseModule):
method setup (line 22) | async def setup(self):
method filter_event (line 28) | async def filter_event(self, event):
method handle_event (line 35) | async def handle_event(self, event):
method url_and_base (line 118) | def url_and_base(self, url):
method getoidc (line 125) | async def getoidc(self, url):
method getoauth (line 147) | async def getoauth(self, url):
FILE: bbot/modules/otx.py
class otx (line 4) | class otx(subdomain_enum_apikey):
method prepare_api_request (line 19) | def prepare_api_request(self, url, kwargs):
method request_url (line 23) | def request_url(self, query):
method parse_results (line 27) | async def parse_results(self, r, query):
FILE: bbot/modules/output/asset_inventory.py
class asset_inventory (line 23) | class asset_inventory(CSV):
method setup (line 65) | async def setup(self):
method filter_event (line 75) | async def filter_event(self, event):
method handle_event (line 86) | async def handle_event(self, event):
method report (line 93) | async def report(self):
method finish (line 163) | async def finish(self):
method _run_hooks (line 222) | def _run_hooks(self):
class Asset (line 235) | class Asset:
method __init__ (line 236) | def __init__(self, host, recheck):
method absorb_csv_row (line 254) | def absorb_csv_row(self, row):
method absorb_event (line 289) | def absorb_event(self, event):
method hostkey (line 348) | def hostkey(self):
method http_status_full (line 352) | def http_status_full(self):
function _make_hostkey (line 356) | def _make_hostkey(host, ips):
function _make_ip_list (line 369) | def _make_ip_list(ips):
FILE: bbot/modules/output/base.py
class BaseOutputModule (line 6) | class BaseOutputModule(BaseModule):
method human_event_str (line 13) | def human_event_str(self, event):
method _event_precheck (line 21) | def _event_precheck(self, event):
method _event_postcheck (line 56) | async def _event_postcheck(self, event):
method is_incoming_duplicate (line 63) | def is_incoming_duplicate(self, event, add=False):
method _prep_output_dir (line 70) | def _prep_output_dir(self, filename):
method _scope_distance_check (line 79) | def _scope_distance_check(self, event):
method file (line 83) | def file(self):
method log (line 89) | def log(self):
FILE: bbot/modules/output/csv.py
class CSV (line 7) | class CSV(BaseOutputModule):
method setup (line 25) | async def setup(self):
method writer (line 33) | def writer(self):
method file (line 40) | def file(self):
method fieldnames (line 48) | def fieldnames(self):
method writerow (line 51) | def writerow(self, row):
method handle_event (line 55) | async def handle_event(self, event):
method cleanup (line 72) | async def cleanup(self):
method report (line 77) | async def report(self):
method add_custom_headers (line 81) | def add_custom_headers(self, headers):
FILE: bbot/modules/output/discord.py
class Discord (line 4) | class Discord(WebhookOutputModule):
FILE: bbot/modules/output/emails.py
class Emails (line 5) | class Emails(TXT):
method setup (line 20) | async def setup(self):
method _scope_distance_check (line 24) | def _scope_distance_check(self, event):
method handle_event (line 27) | async def handle_event(self, event):
method report (line 33) | async def report(self):
FILE: bbot/modules/output/http.py
class HTTP (line 4) | class HTTP(BaseOutputModule):
method setup (line 30) | async def setup(self):
method handle_event (line 52) | async def handle_event(self, event):
FILE: bbot/modules/output/json.py
class JSON (line 7) | class JSON(BaseOutputModule):
method setup (line 21) | async def setup(self):
method handle_event (line 26) | async def handle_event(self, event):
method cleanup (line 33) | async def cleanup(self):
method report (line 38) | async def report(self):
FILE: bbot/modules/output/mysql.py
class MySQL (line 4) | class MySQL(SQLTemplate):
method create_database (line 28) | async def create_database(self):
FILE: bbot/modules/output/neo4j.py
class neo4j (line 13) | class neo4j(BaseOutputModule):
method setup (line 46) | async def setup(self):
method handle_batch (line 61) | async def handle_batch(self, *all_events):
method merge_events (line 101) | async def merge_events(self, events, event_type, id_only=False):
method merge_relationships (line 135) | async def merge_relationships(self, relationships):
method cleanup (line 159) | async def cleanup(self):
FILE: bbot/modules/output/nmap_xml.py
class NmapHost (line 10) | class NmapHost:
method __init__ (line 13) | def __init__(self):
class Nmap_XML (line 19) | class Nmap_XML(BaseOutputModule):
method setup (line 25) | async def setup(self):
method handle_event (line 30) | async def handle_event(self, event):
method report (line 76) | async def report(self):
FILE: bbot/modules/output/postgres.py
class Postgres (line 4) | class Postgres(SQLTemplate):
method create_database (line 28) | async def create_database(self):
FILE: bbot/modules/output/python.py
class python (line 4) | class python(BaseOutputModule):
method _worker (line 8) | async def _worker(self):
FILE: bbot/modules/output/slack.py
class Slack (line 6) | class Slack(WebhookOutputModule):
method format_message_str (line 22) | def format_message_str(self, event):
method format_message_other (line 26) | def format_message_other(self, event):
FILE: bbot/modules/output/splunk.py
class Splunk (line 5) | class Splunk(BaseOutputModule):
method setup (line 27) | async def setup(self):
method handle_event (line 43) | async def handle_event(self, event):
FILE: bbot/modules/output/sqlite.py
class SQLite (line 6) | class SQLite(SQLTemplate):
method setup (line 21) | async def setup(self):
method connection_string (line 32) | def connection_string(self, mask_password=False):
FILE: bbot/modules/output/stdout.py
class Stdout (line 7) | class Stdout(BaseOutputModule):
method setup (line 21) | async def setup(self):
method filter_event (line 34) | async def filter_event(self, event):
method handle_event (line 40) | async def handle_event(self, event):
method handle_text (line 51) | async def handle_text(self, event, event_json):
method handle_json (line 68) | async def handle_json(self, event, event_json):
FILE: bbot/modules/output/subdomains.py
class Subdomains (line 5) | class Subdomains(TXT):
method setup (line 20) | async def setup(self):
method filter_event (line 25) | async def filter_event(self, event):
method _scope_distance_check (line 30) | def _scope_distance_check(self, event):
method handle_event (line 33) | async def handle_event(self, event):
method report (line 39) | async def report(self):
FILE: bbot/modules/output/teams.py
class Teams (line 4) | class Teams(WebhookOutputModule):
method handle_event (line 19) | async def handle_event(self, event):
method trim_message (line 27) | def trim_message(self, message):
method format_message_str (line 32) | def format_message_str(self, event):
method format_message_other (line 39) | def format_message_other(self, event):
method get_severity_color (line 47) | def get_severity_color(self, event):
method format_message (line 61) | def format_message(self, event):
FILE: bbot/modules/output/txt.py
class TXT (line 6) | class TXT(BaseOutputModule):
method setup (line 14) | async def setup(self):
method handle_event (line 18) | async def handle_event(self, event):
method cleanup (line 25) | async def cleanup(self):
method report (line 30) | async def report(self):
FILE: bbot/modules/output/web_parameters.py
class Web_parameters (line 7) | class Web_parameters(BaseOutputModule):
method setup (line 22) | async def setup(self):
method handle_event (line 27) | async def handle_event(self, event):
method cleanup (line 32) | async def cleanup(self):
method report (line 37) | async def report(self):
FILE: bbot/modules/output/web_report.py
class web_report (line 6) | class web_report(BaseOutputModule):
method setup (line 20) | async def setup(self):
method handle_event (line 39) | async def handle_event(self, event):
method report (line 81) | async def report(self):
FILE: bbot/modules/output/websocket.py
class Websocket (line 9) | class Websocket(BaseOutputModule):
method setup (line 20) | async def setup(self):
method handle_event (line 28) | async def handle_event(self, event):
method ws (line 32) | async def ws(self, rebuild=False):
method send (line 49) | async def send(self, message):
method cleanup (line 64) | async def cleanup(self):
FILE: bbot/modules/paramminer_cookies.py
class paramminer_cookies (line 4) | class paramminer_cookies(paramminer_headers):
method check_batch (line 36) | async def check_batch(self, compare_helper, url, cookie_list):
method gen_count_args (line 40) | def gen_count_args(self, url):
FILE: bbot/modules/paramminer_getparams.py
class paramminer_getparams (line 4) | class paramminer_getparams(paramminer_headers):
method check_batch (line 34) | async def check_batch(self, compare_helper, url, getparam_list):
method gen_count_args (line 40) | def gen_count_args(self, url):
FILE: bbot/modules/paramminer_headers.py
class paramminer_headers (line 7) | class paramminer_headers(BaseModule):
method setup_deps (line 85) | async def setup_deps(self):
method setup (line 93) | async def setup(self):
method rand_string (line 109) | def rand_string(self, *args, **kwargs):
method do_mining (line 112) | async def do_mining(self, wl, url, batch_size, compare_helper):
method process_results (line 134) | async def process_results(self, event, results):
method handle_event (line 163) | async def handle_event(self, event):
method count_test (line 202) | async def count_test(self, url):
method gen_count_args (line 213) | def gen_count_args(self, url):
method binary_search (line 224) | async def binary_search(self, compare_helper, url, group, reasons=None...
method check_batch (line 241) | async def check_batch(self, compare_helper, url, header_list):
method finish (line 248) | async def finish(self):
method filter_event (line 265) | async def filter_event(self, event):
FILE: bbot/modules/passivetotal.py
class passivetotal (line 4) | class passivetotal(subdomain_enum_apikey):
method setup (line 19) | async def setup(self):
method ping (line 22) | async def ping(self):
method prepare_api_request (line 29) | def prepare_api_request(self, url, kwargs):
method abort_if (line 34) | async def abort_if(self, event):
method request_url (line 38) | async def request_url(self, query):
method parse_results (line 42) | async def parse_results(self, r, query):
FILE: bbot/modules/pgp.py
class pgp (line 4) | class pgp(subdomain_enum):
method handle_event (line 24) | async def handle_event(self, event):
method query (line 37) | async def query(self, query):
FILE: bbot/modules/portfilter.py
class portfilter (line 4) | class portfilter(BaseInterceptModule):
method setup (line 25) | async def setup(self):
method handle_event (line 35) | async def handle_event(self, event, **kwargs):
FILE: bbot/modules/portscan.py
class portscan (line 12) | class portscan(BaseModule):
method setup (line 52) | async def setup(self):
method handle_batch (line 95) | async def handle_batch(self, *events):
method masscan (line 115) | async def masscan(self, targets, correlator, ping=False):
method make_targets (line 149) | async def make_targets(self, events, scanned_tracker):
method emit_open_port (line 203) | async def emit_open_port(self, ip, port, parent_event):
method parse_json_line (line 229) | def parse_json_line(self, line):
method prep_blacklist (line 253) | def prep_blacklist(self):
method _build_masscan_command (line 266) | def _build_masscan_command(self, target_file=None, ping=False, dry_run...
method log_masscan_status (line 303) | def log_masscan_status(self, s):
method cleanup (line 312) | async def cleanup(self):
FILE: bbot/modules/postman.py
class postman (line 4) | class postman(postman):
method handle_event (line 17) | async def handle_event(self, event):
method process_workspaces (line 41) | async def process_workspaces(self, user=None, org=None):
method query (line 66) | async def query(self, query):
FILE: bbot/modules/postman_download.py
class postman_download (line 7) | class postman_download(postman):
method setup (line 23) | async def setup(self):
method filter_event (line 32) | async def filter_event(self, event):
method handle_event (line 38) | async def handle_event(self, event):
method save_workspace (line 58) | def save_workspace(self, workspace, environments, collections):
method add_json_to_zip (line 83) | def add_json_to_zip(self, zip_path, data, filename):
FILE: bbot/modules/rapiddns.py
class rapiddns (line 4) | class rapiddns(subdomain_enum):
method request_url (line 16) | async def request_url(self, query):
method parse_results (line 21) | async def parse_results(self, r, query):
FILE: bbot/modules/reflected_parameters.py
class reflected_parameters (line 4) | class reflected_parameters(BaseModule):
method handle_event (line 14) | async def handle_event(self, event):
method detect_reflection (line 31) | async def detect_reflection(self, event, url):
method send_probe_with_canary (line 52) | async def send_probe_with_canary(self, event, parameter_name, paramete...
FILE: bbot/modules/report/affiliates.py
class affiliates (line 4) | class affiliates(BaseReportModule):
method setup (line 16) | async def setup(self):
method handle_event (line 20) | async def handle_event(self, event):
method report (line 23) | async def report(self):
method add_affiliate (line 33) | def add_affiliate(self, event):
FILE: bbot/modules/report/asn.py
class asn (line 4) | class asn(BaseReportModule):
method setup (line 18) | async def setup(self):
method filter_event (line 32) | async def filter_event(self, event):
method handle_event (line 39) | async def handle_event(self, event):
method report (line 68) | async def report(self):
method cache_put (line 85) | def cache_put(self, asn):
method cache_get (line 94) | def cache_get(self, ip):
method get_asn (line 105) | async def get_asn(self, ip, retries=1):
method get_asn_ripe (line 124) | async def get_asn_ripe(self, ip):
method get_asn_metadata_ripe (line 147) | async def get_asn_metadata_ripe(self, asn_number):
method get_asn_bgpview (line 185) | async def get_asn_bgpview(self, ip):
method get_emails_bgpview (line 223) | async def get_emails_bgpview(self, asn):
method get_url (line 238) | async def get_url(self, url, data_type, cache=False):
FILE: bbot/modules/report/base.py
class BaseReportModule (line 4) | class BaseReportModule(BaseModule):
FILE: bbot/modules/retirejs.py
class RetireJSSeverity (line 6) | class RetireJSSeverity(IntEnum):
method from_string (line 14) | def from_string(cls, severity_str):
class retirejs (line 21) | class retirejs(BaseModule):
method setup (line 114) | async def setup(self):
method handle_event (line 135) | async def handle_event(self, event):
method filter_event (line 198) | async def filter_event(self, event):
method execute_retirejs (line 204) | async def execute_retirejs(self, js_file):
FILE: bbot/modules/robots.py
class robots (line 4) | class robots(BaseModule):
method setup (line 20) | async def setup(self):
method handle_event (line 23) | async def handle_event(self, event):
FILE: bbot/modules/securitytrails.py
class securitytrails (line 4) | class securitytrails(subdomain_enum_apikey):
method setup (line 20) | async def setup(self):
method request_url (line 24) | async def request_url(self, query):
method parse_results (line 29) | async def parse_results(self, r, query):
FILE: bbot/modules/securitytxt.py
class securitytxt (line 58) | class securitytxt(BaseModule):
method setup (line 76) | async def setup(self):
method _incoming_dedup_hash (line 81) | def _incoming_dedup_hash(self, event):
method filter_event (line 86) | async def filter_event(self, event):
method handle_event (line 91) | async def handle_event(self, event):
FILE: bbot/modules/shodan_dns.py
class shodan_dns (line 4) | class shodan_dns(shodan):
method handle_event (line 19) | async def handle_event(self, event):
method make_url (line 22) | def make_url(self, query):
method parse_results (line 25) | async def parse_results(self, json, query):
FILE: bbot/modules/shodan_idb.py
class shodan_idb (line 5) | class shodan_idb(BaseModule):
method setup (line 64) | async def setup(self):
method _incoming_dedup_hash (line 69) | def _incoming_dedup_hash(self, event):
method api_retries (line 73) | def api_retries(self):
method handle_event (line 77) | async def handle_event(self, event):
method _parse_response (line 114) | async def _parse_response(self, data: dict, event, ip):
method get_ip (line 152) | def get_ip(self, event):
FILE: bbot/modules/sitedossier.py
class sitedossier (line 4) | class sitedossier(subdomain_enum):
method handle_event (line 17) | async def handle_event(self, event):
method query (line 34) | async def query(self, query, parse_fn=None, request_fn=None):
FILE: bbot/modules/skymem.py
class skymem (line 6) | class skymem(emailformat):
method setup (line 19) | async def setup(self):
method handle_event (line 23) | async def handle_event(self, event):
FILE: bbot/modules/smuggler.py
class smuggler (line 11) | class smuggler(BaseModule):
method handle_event (line 27) | async def handle_event(self, event):
FILE: bbot/modules/social.py
class social (line 5) | class social(BaseModule):
method setup (line 33) | async def setup(self):
method handle_event (line 37) | async def handle_event(self, event):
FILE: bbot/modules/sslcert.py
class sslcert (line 11) | class sslcert(BaseModule):
method setup (line 28) | async def setup(self):
method filter_event (line 43) | async def filter_event(self, event):
method handle_event (line 48) | async def handle_event(self, event):
method on_success_callback (line 99) | def on_success_callback(self, event):
method visit_host (line 104) | async def visit_host(self, host, port):
method get_cert_sans (line 172) | def get_cert_sans(cert):
FILE: bbot/modules/subdomaincenter.py
class subdomaincenter (line 4) | class subdomaincenter(subdomain_enum):
method request_url (line 16) | async def request_url(self, query):
method parse_results (line 21) | async def parse_results(self, r, query):
FILE: bbot/modules/subdomainradar.py
class SubdomainRadar (line 7) | class SubdomainRadar(subdomain_enum_apikey):
method setup (line 31) | async def setup(self):
method prepare_api_request (line 70) | def prepare_api_request(self, url, kwargs):
method handle_event (line 75) | async def handle_event(self, event):
method task_poll_loop (line 94) | async def task_poll_loop(self):
method parse_response (line 115) | async def parse_response(self, response, query, event):
method finish (line 132) | async def finish(self):
FILE: bbot/modules/telerik.py
class telerik (line 6) | class telerik(BaseModule):
method normalize_url (line 183) | def normalize_url(url):
method _incoming_dedup_hash (line 186) | def _incoming_dedup_hash(self, event):
method handle_event (line 195) | async def handle_event(self, event):
method create_url (line 386) | def create_url(self, baseurl, detector):
method test_detector (line 389) | async def test_detector(self, baseurl, detector):
method filter_event (line 395) | async def filter_event(self, event):
FILE: bbot/modules/templates/bucket.py
class bucket_template (line 7) | class bucket_template(BaseModule):
method setup (line 23) | async def setup(self):
method filter_event (line 35) | async def filter_event(self, event):
method filter_bucket (line 44) | def filter_bucket(self, event):
method handle_event (line 49) | async def handle_event(self, event):
method handle_dns_name (line 55) | async def handle_dns_name(self, event):
method handle_storage_bucket (line 73) | async def handle_storage_bucket(self, event):
method emit_storage_bucket (line 99) | async def emit_storage_bucket(self, event_data, event_type, parent, ta...
method brute_buckets (line 109) | async def brute_buckets(self, buckets, permutations=False, omit_base=F...
method clean_bucket_url (line 134) | def clean_bucket_url(self, url):
method build_bucket_request (line 138) | def build_bucket_request(self, bucket_name, base_domain, region):
method _check_bucket_exists (line 142) | def _check_bucket_exists(self, bucket_name, response):
method check_bucket_exists (line 146) | def check_bucket_exists(self, bucket_name, response):
method _check_bucket_open (line 152) | async def _check_bucket_open(self, bucket_name, url):
method check_bucket_open (line 156) | async def check_bucket_open(self, bucket_name, url):
method valid_bucket_name (line 167) | def valid_bucket_name(self, bucket_name):
method is_valid_bucket_name (line 176) | def is_valid_bucket_name(self, bucket_name):
method bucket_name_regexes (line 180) | def bucket_name_regexes(self):
method build_url (line 187) | def build_url(self, bucket_name, base_domain, region):
method gen_tags_exists (line 190) | def gen_tags_exists(self, response):
method gen_tags_open (line 193) | def gen_tags_open(self, response):
FILE: bbot/modules/templates/censys.py
class censys (line 6) | class censys(subdomain_enum_apikey):
method setup (line 17) | async def setup(self):
method ping (line 41) | async def ping(self):
method prepare_api_request (line 51) | def prepare_api_request(self, url, kwargs):
FILE: bbot/modules/templates/github.py
class github (line 6) | class github(BaseModule):
method prepare_api_request (line 16) | def prepare_api_request(self, url, kwargs):
method setup (line 20) | async def setup(self):
method github_graphql_request (line 48) | async def github_graphql_request(self, graphql_query, resp_key):
FILE: bbot/modules/templates/gitlab.py
class GitLabBaseModule (line 4) | class GitLabBaseModule(BaseModule):
method setup (line 18) | async def setup(self):
method handle_social (line 23) | async def handle_social(self, event):
method handle_projects_url (line 38) | async def handle_projects_url(self, projects_url, event):
method handle_groups_url (line 51) | async def handle_groups_url(self, groups_url, event):
method gitlab_json_request (line 55) | async def gitlab_json_request(self, url):
method handle_namespace (line 67) | async def handle_namespace(self, namespace, event):
method get_base_url (line 94) | def get_base_url(self, event):
FILE: bbot/modules/templates/postman.py
class postman (line 4) | class postman(BaseModule):
method setup (line 24) | async def setup(self):
method prepare_api_request (line 53) | def prepare_api_request(self, url, kwargs):
method get_workspace_id (line 58) | async def get_workspace_id(self, repo_url):
method request_workspace (line 82) | async def request_workspace(self, id):
method get_workspace (line 114) | async def get_workspace(self, workspace_id):
method get_globals (line 129) | async def get_globals(self, workspace_id):
method get_environment (line 144) | async def get_environment(self, environment_id):
method get_collection (line 159) | async def get_collection(self, collection_id):
method validate_workspace (line 174) | async def validate_workspace(self, workspace, environments, collections):
FILE: bbot/modules/templates/shodan.py
class shodan (line 6) | class shodan(subdomain_enum):
method setup (line 13) | async def setup(self):
FILE: bbot/modules/templates/sql.py
class SQLTemplate (line 10) | class SQLTemplate(BaseOutputModule):
method setup (line 29) | async def setup(self):
method handle_event (line 39) | async def handle_event(self, event):
method create_database (line 63) | async def create_database(self):
method init_database (line 66) | async def init_database(self):
method connection_string (line 78) | def connection_string(self, mask_password=False):
method cleanup (line 93) | async def cleanup(self):
FILE: bbot/modules/templates/subdomain_enum.py
class subdomain_enum (line 4) | class subdomain_enum(BaseModule):
method source_pretty_name (line 40) | def source_pretty_name(self):
method _incoming_dedup_hash (line 43) | def _incoming_dedup_hash(self, event):
method handle_event (line 49) | async def handle_event(self, event):
method handle_event_paginated (line 69) | async def handle_event_paginated(self, event):
method request_url (line 87) | async def request_url(self, query):
method make_url (line 91) | def make_url(self, query):
method make_query (line 94) | def make_query(self, event):
method parse_results (line 109) | async def parse_results(self, r, query=None):
method query (line 115) | async def query(self, query, request_fn=None, parse_fn=None):
method query_paginated (line 142) | async def query_paginated(self, query):
method _is_wildcard (line 155) | async def _is_wildcard(self, query):
method filter_event (line 163) | async def filter_event(self, event):
method abort_if (line 186) | async def abort_if(self, event):
class subdomain_enum_apikey (line 193) | class subdomain_enum_apikey(subdomain_enum):
method setup (line 206) | async def setup(self):
FILE: bbot/modules/templates/webhook.py
class WebhookOutputModule (line 6) | class WebhookOutputModule(BaseOutputModule):
method setup (line 21) | async def setup(self):
method api_retries (line 34) | def api_retries(self):
method handle_event (line 37) | async def handle_event(self, event):
method get_watched_events (line 46) | def get_watched_events(self):
method filter_event (line 54) | async def filter_event(self, event):
method format_message_str (line 61) | def format_message_str(self, event):
method format_message_other (line 65) | def format_message_other(self, event):
method get_severity_color (line 73) | def get_severity_color(self, event):
method format_message (line 80) | def format_message(self, event):
method evaluate_response (line 89) | def evaluate_response(self, response):
FILE: bbot/modules/trickest.py
class Trickest (line 4) | class Trickest(subdomain_enum_apikey):
method prepare_api_request (line 26) | def prepare_api_request(self, url, kwargs):
method handle_event (line 30) | async def handle_event(self, event):
method make_url (line 33) | def make_url(self, query):
method parse_results (line 39) | async def parse_results(self, j, query):
FILE: bbot/modules/trufflehog.py
class trufflehog (line 6) | class trufflehog(BaseModule):
method setup_deps (line 44) | async def setup_deps(self):
method setup (line 50) | async def setup(self):
method filter_event (line 73) | async def filter_event(self, event):
method handle_event (line 87) | async def handle_event(self, event):
method execute_trufflehog (line 150) | async def execute_trufflehog(self, module, path=None, string=None):
method log_trufflehog_status (line 206) | def log_trufflehog_status(self, path, line):
FILE: bbot/modules/url_manipulation.py
class url_manipulation (line 5) | class url_manipulation(BaseModule):
method setup (line 21) | async def setup(self):
method handle_event (line 45) | async def handle_event(self, event):
method filter_event (line 92) | async def filter_event(self, event):
method format_signature (line 100) | def format_signature(self, sig, event):
FILE: bbot/modules/urlscan.py
class urlscan (line 4) | class urlscan(subdomain_enum):
method setup (line 18) | async def setup(self):
method handle_event (line 22) | async def handle_event(self, event):
method query (line 57) | async def query(self, query):
FILE: bbot/modules/vhost.py
class vhost (line 7) | class vhost(ffuf):
method setup (line 30) | async def setup(self):
method handle_event (line 35) | async def handle_event(self, event):
method ffuf_vhost (line 74) | async def ffuf_vhost(self, host, basehost, event, wordlist=None, skip_...
method mutations_check (line 104) | def mutations_check(self, vhost):
method finish (line 112) | async def finish(self):
FILE: bbot/modules/viewdns.py
class viewdns (line 6) | class viewdns(BaseModule):
method setup (line 24) | async def setup(self):
method handle_event (line 28) | async def handle_event(self, event):
method query (line 39) | async def query(self, query):
FILE: bbot/modules/virustotal.py
class virustotal (line 4) | class virustotal(subdomain_enum_apikey):
method make_url (line 20) | def make_url(self, query):
method prepare_api_request (line 23) | def prepare_api_request(self, url, kwargs):
method parse_results (line 27) | async def parse_results(self, r, query):
FILE: bbot/modules/wafw00f.py
class wafw00f (line 11) | class wafw00f(BaseModule):
method filter_event (line 33) | async def filter_event(self, event):
method _incoming_dedup_hash (line 39) | def _incoming_dedup_hash(self, event):
method handle_event (line 42) | async def handle_event(self, event):
FILE: bbot/modules/wayback.py
class wayback (line 6) | class wayback(subdomain_enum):
method setup (line 24) | async def setup(self):
method handle_event (line 29) | async def handle_event(self, event):
method query (line 40) | async def query(self, query):
FILE: bbot/modules/wpscan.py
class wpscan (line 5) | class wpscan(BaseModule):
method setup (line 66) | async def setup(self):
method filter_event (line 79) | async def filter_event(self, event):
method handle_event (line 93) | async def handle_event(self, event):
method handle_http_response (line 99) | async def handle_http_response(self, source_event):
method handle_technology (line 106) | async def handle_technology(self, source_event):
method construct_command (line 113) | def construct_command(self, url):
method parse_wpscan_output (line 142) | def parse_wpscan_output(self, output, base_url, source_event):
method parse_wp_misc (line 157) | def parse_wp_misc(self, interesting_json, base_url, source_event):
method parse_wp_version (line 182) | def parse_wp_version(self, version_json, url, source_event):
method parse_wp_themes (line 205) | def parse_wp_themes(self, theme_json, url, source_event):
method parse_wp_plugins (line 230) | def parse_wp_plugins(self, plugins_json, base_url, source_event):
method vulnerability_to_s (line 259) | def vulnerability_to_s(self, vuln_json):
method get_base_url (line 281) | def get_base_url(self, event):
FILE: bbot/scanner/dispatcher.py
class Dispatcher (line 7) | class Dispatcher:
method set_scan (line 12) | def set_scan(self, scan):
method on_start (line 15) | async def on_start(self, scan):
method on_finish (line 18) | async def on_finish(self, scan):
method on_status (line 21) | async def on_status(self, status, scan_id):
method catch (line 27) | async def catch(self, callback, *args, **kwargs):
FILE: bbot/scanner/manager.py
class ScanIngress (line 8) | class ScanIngress(BaseInterceptModule):
method priority (line 22) | def priority(self):
method __init__ (line 26) | def __init__(self, *args, **kwargs):
method init_events (line 33) | async def init_events(self, event_seeds=None):
method handle_event (line 70) | async def handle_event(self, event, **kwargs):
method non_intercept_modules (line 126) | def non_intercept_modules(self):
method incoming_queues (line 132) | def incoming_queues(self):
method module_priority_weights (line 137) | def module_priority_weights(self):
method get_incoming_event (line 144) | async def get_incoming_event(self):
method is_incoming_duplicate (line 152) | def is_incoming_duplicate(self, event, add=False):
class ScanEgress (line 171) | class ScanEgress(BaseInterceptModule):
method priority (line 183) | def priority(self):
method handle_event (line 187) | async def handle_event(self, event, **kwargs):
method forward_event (line 259) | async def forward_event(self, event, kwargs):
FILE: bbot/scanner/preset/args.py
class BBOTArgs (line 19) | class BBOTArgs:
method __init__ (line 91) | def __init__(self, preset):
method parsed (line 99) | def parsed(self):
method preset_from_args (line 105) | def preset_from_args(self):
method create_parser (line 217) | def create_parser(self, *args, **kwargs):
method sanitize_args (line 403) | def sanitize_args(self):
method validate (line 462) | def validate(self):
FILE: bbot/scanner/preset/conditions.py
class ConditionEvaluator (line 10) | class ConditionEvaluator:
method __init__ (line 11) | def __init__(self, preset):
method context (line 15) | def context(self):
method abort (line 23) | def abort(self, message):
method warn (line 27) | def warn(self, message):
method evaluate (line 30) | def evaluate(self):
method jinja_env (line 43) | def jinja_env(self):
method check_condition (line 51) | def check_condition(self, condition_str, context):
FILE: bbot/scanner/preset/environ.py
function increase_limit (line 18) | def increase_limit(new_limit):
function env_resolver (line 37) | def env_resolver(env_name, default=None):
function add_to_path (line 41) | def add_to_path(v, k="PATH", environ=None):
class BBOTEnviron (line 73) | class BBOTEnviron:
method __init__ (line 74) | def __init__(self, preset):
method flatten_config (line 77) | def flatten_config(self, config, base="bbot"):
method prepare (line 90) | def prepare(self):
FILE: bbot/scanner/preset/path.py
class PresetPath (line 12) | class PresetPath:
method __init__ (line 17) | def __init__(self):
method find (line 20) | def find(self, filename):
method __str__ (line 47) | def __str__(self):
method add_path (line 50) | def add_path(self, path):
method __iter__ (line 66) | def __iter__(self):
FILE: bbot/scanner/preset/preset.py
class BasePreset (line 27) | class BasePreset(type):
method __call__ (line 28) | def __call__(cls, *args, include=None, presets=None, name=None, descri...
class Preset (line 59) | class Preset(metaclass=BasePreset):
method __init__ (line 116) | def __init__(
method bbot_home (line 280) | def bbot_home(self):
method target (line 284) | def target(self):
method seeds (line 290) | def seeds(self):
method whitelist (line 296) | def whitelist(self):
method blacklist (line 302) | def blacklist(self):
method preset_dir (line 308) | def preset_dir(self):
method default_output_modules (line 312) | def default_output_modules(self):
method default_internal_modules (line 322) | def default_internal_modules(self):
method merge (line 330) | def merge(self, other):
method bake (line 401) | def bake(self, scan=None):
method parse_args (line 503) | def parse_args(self):
method module_dirs (line 513) | def module_dirs(self):
method module_dirs (line 517) | def module_dirs(self, module_dirs):
method scan_modules (line 526) | def scan_modules(self):
method output_modules (line 530) | def output_modules(self):
method internal_modules (line 534) | def internal_modules(self):
method add_module (line 537) | def add_module(self, module_name, module_type="scan", raise_error=True):
method preloaded_module (line 549) | def preloaded_module(self, module):
method config (line 553) | def config(self):
method web_config (line 557) | def web_config(self):
method scope_config (line 561) | def scope_config(self):
method strict_scope (line 565) | def strict_scope(self):
method apply_log_level (line 568) | def apply_log_level(self, apply_core=False):
method helpers (line 598) | def helpers(self):
method module_loader (line 606) | def module_loader(self):
method environ (line 617) | def environ(self):
method args (line 625) | def args(self):
method in_scope (line 632) | def in_scope(self, host):
method blacklisted (line 635) | def blacklisted(self, host):
method whitelisted (line 638) | def whitelisted(self, host):
method from_dict (line 642) | def from_dict(cls, preset_dict, name=None, _exclude=None, _log=False):
method include_preset (line 684) | def include_preset(self, filename):
method from_yaml_file (line 705) | def from_yaml_file(cls, filename, _exclude=None, _log=False):
method from_yaml_string (line 739) | def from_yaml_string(cls, yaml_preset):
method to_dict (line 755) | def to_dict(self, include_target=False, full_config=False, redact_secr...
method to_yaml (line 835) | def to_yaml(self, include_target=False, full_config=False, sort_keys=F...
method _is_valid_module (line 858) | def _is_valid_module(self, module, module_type, name_only=False, raise...
method validate (line 902) | def validate(self):
method all_presets (line 929) | def all_presets(self):
method presets_table (line 980) | def presets_table(self, include_modules=True):
method log_verbose (line 997) | def log_verbose(self, msg):
method log_debug (line 1001) | def log_debug(self, msg):
FILE: bbot/scanner/scanner.py
class Scanner (line 25) | class Scanner:
method __init__ (line 99) | def __init__(
method _prep (line 271) | async def _prep(self):
method start (line 331) | def start(self):
method start_without_generator (line 335) | def start_without_generator(self):
method async_start_without_generator (line 339) | async def async_start_without_generator(self):
method async_start (line 343) | async def async_start(self):
method _mark_finished (line 451) | async def _mark_finished(self):
method _start_modules (line 482) | def _start_modules(self):
method setup_modules (line 487) | async def setup_modules(self, remove_failed=True, deps_only=False):
method load_modules (line 530) | async def load_modules(self):
method modules_finished (line 622) | def modules_finished(self):
method kill_module (line 626) | def kill_module(self, module_name, message=None):
method incoming_event_queues (line 640) | def incoming_event_queues(self):
method num_queued_events (line 644) | def num_queued_events(self):
method modules_status (line 650) | def modules_status(self, _log=False):
method stop (line 753) | def stop(self):
method finish (line 773) | async def finish(self):
method _drain_queues (line 800) | def _drain_queues(self):
method _cancel_tasks (line 820) | def _cancel_tasks(self):
method _report (line 850) | async def _report(self):
method _cleanup (line 867) | async def _cleanup(self):
method in_scope (line 898) | def in_scope(self, *args, **kwargs):
method whitelisted (line 901) | def whitelisted(self, *args, **kwargs):
method blacklisted (line 904) | def blacklisted(self, *args, **kwargs):
method core (line 908) | def core(self):
method config (line 912) | def config(self):
method target (line 916) | def target(self):
method seeds (line 920) | def seeds(self):
method whitelist (line 924) | def whitelist(self):
method blacklist (line 928) | def blacklist(self):
method helpers (line 932) | def helpers(self):
method force_start (line 936) | def force_start(self):
method word_cloud (line 940) | def word_cloud(self):
method stopping (line 944) | def stopping(self):
method stopped (line 948) | def stopped(self):
method running (line 952) | def running(self):
method aborting (line 956) | def aborting(self):
method status (line 960) | def status(self):
method omitted_event_types (line 964) | def omitted_event_types(self):
method status (line 970) | def status(self, status):
method make_event (line 993) | def make_event(self, *args, **kwargs):
method update_event (line 998) | def update_event(self, event, **kwargs):
method root_event (line 1003) | def root_event(self):
method finish_event (line 1028) | def finish_event(self, context=None, status=None):
method make_root_event (line 1036) | def make_root_event(self, context):
method dns_strings (line 1046) | def dns_strings(self):
method _generate_dns_regexes (line 1062) | def _generate_dns_regexes(self, pattern):
method dns_regexes (line 1081) | def dns_regexes(self):
method dns_regexes_yara (line 1097) | def dns_regexes_yara(self):
method dns_yara_rules_uncompiled (line 1106) | def dns_yara_rules_uncompiled(self):
method dns_yara_rules (line 1126) | async def dns_yara_rules(self):
method extract_in_scope_hostnames (line 1136) | async def extract_in_scope_hostnames(self, s):
method json (line 1154) | def json(self):
method debug (line 1175) | def debug(self, *args, trace=False, **kwargs):
method verbose (line 1180) | def verbose(self, *args, trace=False, **kwargs):
method hugeverbose (line 1185) | def hugeverbose(self, *args, trace=False, **kwargs):
method info (line 1190) | def info(self, *args, trace=False, **kwargs):
method hugeinfo (line 1195) | def hugeinfo(self, *args, trace=False, **kwargs):
method success (line 1200) | def success(self, *args, trace=False, **kwargs):
method hugesuccess (line 1205) | def hugesuccess(self, *args, trace=False, **kwargs):
method warning (line 1210) | def warning(self, *args, trace=True, **kwargs):
method hugewarning (line 1215) | def hugewarning(self, *args, trace=True, **kwargs):
method error (line 1220) | def error(self, *args, trace=True, **kwargs):
method trace (line 1225) | def trace(self, msg=None):
method critical (line 1233) | def critical(self, *args, trace=True, **kwargs):
method log_level (line 1239) | def log_level(self):
method _log_handlers (line 1246) | def _log_handlers(self):
method _start_log_handlers (line 1264) | def _start_log_handlers(self):
method _stop_log_handlers (line 1275) | def _stop_log_handlers(self):
method _fail_setup (line 1283) | def _fail_setup(self, msg):
method _load_modules (line 1291) | def _load_modules(self, modules):
method _status_ticker (line 1308) | async def _status_ticker(self, interval=15):
method _acatch (line 1315) | async def _acatch(self, context="scan", finally_callback=None, unhandl...
method _handle_exception (line 1331) | def _handle_exception(self, e, context="scan", finally_callback=None, ...
method _make_dummy_module (line 1355) | def _make_dummy_module(self, name, _type="scan"):
class DummyModule (line 1370) | class DummyModule(BaseModule):
method __init__ (line 1373) | def __init__(self, *args, **kwargs):
FILE: bbot/scanner/stats.py
function _increment (line 8) | def _increment(d, k):
class SpeedCounter (line 15) | class SpeedCounter:
method __init__ (line 20) | def __init__(self, window=60):
method tick (line 24) | def tick(self):
method remove_old_timestamps (line 29) | def remove_old_timestamps(self, current_time):
method speed (line 34) | def speed(self):
class ScanStats (line 39) | class ScanStats:
method __init__ (line 40) | def __init__(self, scan):
method event_produced (line 46) | def event_produced(self, event):
method event_consumed (line 52) | def event_consumed(self, event, module):
method get (line 61) | def get(self, module):
method table (line 71) | def table(self):
method _make_table (line 93) | def _make_table(self):
class ModuleStat (line 100) | class ModuleStat:
method __init__ (line 101) | def __init__(self, module):
method increment_produced (line 108) | def increment_produced(self, event):
method increment_consumed (line 112) | def increment_consumed(self, event):
FILE: bbot/scanner/target.py
class BaseTarget (line 15) | class BaseTarget(RadixTarget):
method __init__ (line 30) | def __init__(self, *targets, **kwargs):
method inputs (line 37) | def inputs(self):
method get (line 40) | def get(self, event, **kwargs):
method _make_event_seed (line 70) | def _make_event_seed(self, target, raise_error=False):
method add (line 80) | def add(self, targets, data=None):
method __iter__ (line 97) | def __iter__(self):
class ScanSeeds (line 101) | class ScanSeeds(BaseTarget):
method get (line 108) | def get(self, event, single=True, **kwargs):
method _add (line 114) | def _add(self, host, data):
method _hash_value (line 131) | def _hash_value(self):
class ACLTarget (line 136) | class ACLTarget(BaseTarget):
method __init__ (line 137) | def __init__(self, *args, **kwargs):
class ScanWhitelist (line 143) | class ScanWhitelist(ACLTarget):
class ScanBlacklist (line 151) | class ScanBlacklist(ACLTarget):
method __init__ (line 158) | def __init__(self, *args, **kwargs):
method get (line 162) | def get(self, host, **kwargs):
method _add (line 190) | def _add(self, host, data):
method _hash_value (line 196) | def _hash_value(self):
method __len__ (line 202) | def __len__(self):
method __bool__ (line 205) | def __bool__(self):
class BBOTTarget (line 209) | class BBOTTarget:
method __init__ (line 219) | def __init__(self, *seeds, whitelist=None, blacklist=None, strict_scop...
method json (line 230) | def json(self):
method hash (line 244) | def hash(self):
method scope_hash (line 251) | def scope_hash(self):
method in_scope (line 258) | def in_scope(self, host):
method blacklisted (line 275) | def blacklisted(self, host):
method whitelisted (line 291) | def whitelisted(self, host):
method __eq__ (line 307) | def __eq__(self, other):
FILE: bbot/scripts/benchmark_report.py
function run_command (line 17) | def run_command(cmd: List[str], cwd: Path = None, capture_output: bool =...
function get_current_branch (line 29) | def get_current_branch() -> str:
function checkout_branch (line 35) | def checkout_branch(branch: str, repo_path: Path = None):
function run_benchmarks (line 41) | def run_benchmarks(output_file: Path, repo_path: Path = None) -> bool:
function load_benchmark_data (line 71) | def load_benchmark_data(filepath: Path) -> Dict[str, Any]:
function format_time (line 84) | def format_time(seconds: float) -> str:
function format_ops (line 96) | def format_ops(ops: float) -> str:
function calculate_change_percentage (line 104) | def calculate_change_percentage(old_value: float, new_value: float) -> T...
function generate_benchmark_table (line 119) | def generate_benchmark_table(benchmarks: List[Dict[str, Any]], title: st...
function generate_comparison_table (line 146) | def generate_comparison_table(current_data: Dict, base_data: Dict, curre...
function generate_report (line 299) | def generate_report(current_data: Dict, base_data: Dict, current_branch:...
function main (line 354) | def main():
FILE: bbot/scripts/docs.py
function gen_chord_data (line 26) | def gen_chord_data():
function homedir_collapseuser (line 94) | def homedir_collapseuser(f):
function enclose_tags (line 102) | def enclose_tags(text):
function find_replace_markdown (line 108) | def find_replace_markdown(content, keyword, replace):
function find_replace_file (line 123) | def find_replace_file(file, keyword, replace):
function update_docs (line 133) | def update_docs():
FILE: bbot/test/bbot_fixtures.py
function tempwordlist (line 35) | def tempwordlist(content):
function tempapkfile (line 44) | def tempapkfile():
function clean_default_config (line 52) | def clean_default_config(monkeypatch):
class SubstringRequestMatcher (line 61) | class SubstringRequestMatcher(pytest_httpserver.httpserver.RequestMatcher):
method match_data (line 62) | def match_data(self, request: Request) -> bool:
function bbot_scanner (line 78) | def bbot_scanner():
function scan (line 85) | def scan():
function helpers (line 96) | def helpers(scan):
function events (line 148) | def events(scan):
FILE: bbot/test/benchmarks/test_bloom_filter_benchmarks.py
class TestBloomFilterBenchmarks (line 7) | class TestBloomFilterBenchmarks:
method setup_method (line 15) | def setup_method(self):
method _generate_random_strings (line 23) | def _generate_random_strings(self, n, length=10):
method test_bloom_filter_dns_mutation_tracking_performance (line 30) | def test_bloom_filter_dns_mutation_tracking_performance(self, benchmark):
method test_bloom_filter_large_scale_dns_brute_force (line 67) | def test_bloom_filter_large_scale_dns_brute_force(self, benchmark):
FILE: bbot/test/benchmarks/test_closest_match_benchmarks.py
class TestClosestMatchBenchmarks (line 6) | class TestClosestMatchBenchmarks:
method setup_method (line 15) | def setup_method(self):
method _generate_large_closest_match_choices (line 24) | def _generate_large_closest_match_choices(self):
method _generate_realistic_closest_match_choices (line 34) | def _generate_realistic_closest_match_choices(self):
method test_large_closest_match_lookup (line 59) | def test_large_closest_match_lookup(self, benchmark):
method test_realistic_closest_match_workload (line 69) | def test_realistic_closest_match_workload(self, benchmark):
FILE: bbot/test/benchmarks/test_event_validation_benchmarks.py
class TestEventValidationBenchmarks (line 8) | class TestEventValidationBenchmarks:
method setup_method (line 9) | def setup_method(self):
method _generate_diverse_targets (line 22) | def _generate_diverse_targets(self, count=1000):
method _generate_diverse_event_data (line 138) | def _generate_diverse_event_data(self, count=1000):
method test_event_validation_full_scan_startup_small_batch (line 257) | def test_event_validation_full_scan_startup_small_batch(self, benchmark):
method test_event_validation_full_scan_startup_large_batch (line 284) | def test_event_validation_full_scan_startup_large_batch(self, benchmark):
method test_make_event_autodetection_small (line 331) | def test_make_event_autodetection_small(self, benchmark):
method test_make_event_autodetection_large (line 366) | def test_make_event_autodetection_large(self, benchmark):
method test_make_event_explicit_types (line 407) | def test_make_event_explicit_types(self, benchmark):
FILE: bbot/test/benchmarks/test_excavate_benchmarks.py
class TestExcavateDirectBenchmarks (line 6) | class TestExcavateDirectBenchmarks:
method _generate_text_segments (line 22) | def _generate_text_segments(self, target_size, count):
method _generate_realistic_content (line 45) | def _generate_realistic_content(self, index):
method _run_excavate_single_thread (line 99) | async def _run_excavate_single_thread(self, text_segments):
method _run_excavate_parallel_tasks (line 140) | async def _run_excavate_parallel_tasks(self, text_segments):
method test_excavate_single_thread_small (line 179) | def test_excavate_single_thread_small(self, benchmark):
method test_excavate_single_thread_large (line 216) | def test_excavate_single_thread_large(self, benchmark):
method test_excavate_parallel_tasks_small (line 254) | def test_excavate_parallel_tasks_small(self, benchmark):
method test_excavate_parallel_tasks_large (line 274) | def test_excavate_parallel_tasks_large(self, benchmark):
FILE: bbot/test/benchmarks/test_ipaddress_benchmarks.py
class TestIPAddressBenchmarks (line 7) | class TestIPAddressBenchmarks:
method setup_method (line 16) | def setup_method(self):
method _generate_valid_ips (line 26) | def _generate_valid_ips(self):
method _generate_invalid_ips (line 58) | def _generate_invalid_ips(self):
method _generate_mixed_data (line 86) | def _generate_mixed_data(self):
method test_is_ip_performance (line 97) | def test_is_ip_performance(self, benchmark):
method test_make_ip_type_performance (line 111) | def test_make_ip_type_performance(self, benchmark):
method test_mixed_ip_operations (line 128) | def test_mixed_ip_operations(self, benchmark):
FILE: bbot/test/benchmarks/test_weighted_shuffle_benchmarks.py
class TestWeightedShuffleBenchmarks (line 6) | class TestWeightedShuffleBenchmarks:
method setup_method (line 15) | def setup_method(self):
method _generate_small_dataset (line 26) | def _generate_small_dataset(self):
method _generate_medium_dataset (line 30) | def _generate_medium_dataset(self):
method _generate_large_dataset (line 36) | def _generate_large_dataset(self):
method _generate_priority_weights (line 42) | def _generate_priority_weights(self):
method test_typical_queue_shuffle (line 51) | def test_typical_queue_shuffle(self, benchmark):
method test_priority_queue_shuffle (line 62) | def test_priority_queue_shuffle(self, benchmark):
FILE: bbot/test/conftest.py
function assert_all_responses_were_requested (line 46) | def assert_all_responses_were_requested() -> bool:
function silence_live_logging (line 51) | def silence_live_logging():
function stop_server (line 57) | def stop_server(server):
function bbot_httpserver (line 64) | def bbot_httpserver():
function bbot_httpserver_ssl (line 78) | def bbot_httpserver_ssl():
function should_mock (line 96) | def should_mock(request):
function pytest_collection_modifyitems (line 100) | def pytest_collection_modifyitems(config, items):
function bbot_httpserver_allinterfaces (line 114) | def bbot_httpserver_allinterfaces():
class Interactsh_mock (line 127) | class Interactsh_mock:
method __init__ (line 128) | def __init__(self, name):
method mock_interaction (line 136) | def mock_interaction(self, subdomain_tag, msg=None):
method register (line 142) | async def register(self, callback=None):
method deregister (line 147) | async def deregister(self, callback=None):
method poll_loop (line 155) | async def poll_loop(self, callback=None):
method poll (line 164) | async def poll(self, callback=None):
class Proxy (line 183) | class Proxy(http.server.SimpleHTTPRequestHandler):
method do_GET (line 188) | def do_GET(self):
function proxy_server (line 212) | def proxy_server():
function pytest_terminal_summary (line 227) | def pytest_terminal_summary(terminalreporter, exitstatus, config): # pr...
function _print_detailed_info (line 263) | def _print_detailed_info(): # pragma: no cover
function pytest_sessionfinish (line 335) | def pytest_sessionfinish(session, exitstatus):
FILE: bbot/test/fastapi_test.py
function start (line 9) | async def start(targets: List[str] = Query(...)):
function ping (line 16) | async def ping():
FILE: bbot/test/test_step_1/test__module__tests.py
function test__module__tests (line 17) | def test__module__tests():
FILE: bbot/test/test_step_1/test_bbot_fastapi.py
function run_bbot_multiprocess (line 11) | def run_bbot_multiprocess(queue):
function test_bbot_multiprocess (line 19) | def test_bbot_multiprocess(bbot_httpserver):
function test_bbot_fastapi (line 33) | def test_bbot_fastapi(bbot_httpserver):
function start_fastapi_server (line 68) | def start_fastapi_server():
FILE: bbot/test/test_step_1/test_bloom_filter.py
function test_bloom_filter (line 8) | async def test_bloom_filter():
FILE: bbot/test/test_step_1/test_cli.py
function test_cli_scope (line 9) | async def test_cli_scope(monkeypatch, capsys):
function test_cli_scan (line 89) | async def test_cli_scan(monkeypatch):
function test_cli_args (line 139) | async def test_cli_args(monkeypatch, caplog, capsys, clean_default_config):
function test_cli_customheaders (line 427) | async def test_cli_customheaders(monkeypatch, caplog, capsys):
function test_cli_module_help (line 463) | async def test_cli_module_help(monkeypatch, capsys):
function test_cli_config_validation (line 476) | def test_cli_config_validation(monkeypatch, caplog):
function test_cli_module_validation (line 497) | def test_cli_module_validation(monkeypatch, caplog):
function test_cli_presets (line 621) | def test_cli_presets(monkeypatch, capsys, caplog):
FILE: bbot/test/test_step_1/test_command.py
function test_command (line 7) | async def test_command(bbot_scanner):
FILE: bbot/test/test_step_1/test_config.py
function test_config (line 5) | async def test_config(bbot_scanner):
FILE: bbot/test/test_step_1/test_depsinstaller.py
function test_depsinstaller (line 5) | async def test_depsinstaller(monkeypatch, bbot_scanner):
FILE: bbot/test/test_step_1/test_dns.py
function test_dns_engine (line 19) | async def test_dns_engine(bbot_scanner):
function test_dns_resolution (line 66) | async def test_dns_resolution(bbot_scanner):
function test_wildcards (line 187) | async def test_wildcards(bbot_scanner):
function test_wildcard_deduplication (line 635) | async def test_wildcard_deduplication(bbot_scanner):
function test_dns_raw_records (line 670) | async def test_dns_raw_records(bbot_scanner):
function test_dns_graph_structure (line 752) | async def test_dns_graph_structure(bbot_scanner):
function test_hostname_extraction (line 780) | async def test_hostname_extraction(bbot_scanner):
function test_dns_helpers (line 813) | async def test_dns_helpers(bbot_scanner):
FILE: bbot/test/test_step_1/test_docs.py
function test_docs (line 1) | def test_docs():
FILE: bbot/test/test_step_1/test_engine.py
function test_engine (line 5) | async def test_engine():
FILE: bbot/test/test_step_1/test_event_seeds.py
function test_event_seeds (line 7) | def test_event_seeds():
FILE: bbot/test/test_step_1/test_events.py
function test_events (line 11) | async def test_events(events, helpers):
function test_event_discovery_context (line 634) | async def test_event_discovery_context():
function test_event_web_spider_distance (line 809) | async def test_event_web_spider_distance(bbot_scanner):
function test_event_confidence (line 900) | def test_event_confidence():
function test_event_closest_host (line 935) | def test_event_closest_host():
function test_event_magic (line 989) | def test_event_magic():
function test_mobile_app (line 1031) | async def test_mobile_app():
function test_filesystem (line 1059) | async def test_filesystem():
function test_event_hashing (line 1069) | def test_event_hashing():
FILE: bbot/test/test_step_1/test_files.py
function test_files (line 7) | async def test_files(bbot_scanner):
FILE: bbot/test/test_step_1/test_helpers.py
function test_helpers_misc (line 9) | async def test_helpers_misc(helpers, scan, bbot_scanner, bbot_httpserver):
function test_word_cloud (line 647) | async def test_word_cloud(helpers, bbot_scanner):
function test_names (line 749) | def test_names(helpers):
function test_ratelimiter (line 755) | async def test_ratelimiter(helpers):
function test_sync_to_async (line 778) | def test_sync_to_async():
function test_async_helpers (line 799) | async def test_async_helpers():
function test_portparse (line 816) | def test_portparse(helpers):
function test_liststring_valid_strings (line 846) | def test_liststring_valid_strings(helpers):
function test_liststring_invalid_string (line 850) | def test_liststring_invalid_string(helpers):
function test_liststring_singleitem (line 856) | def test_liststring_singleitem(helpers):
function test_liststring_invalidfnchars (line 860) | def test_liststring_invalidfnchars(helpers):
function test_parameter_validation (line 868) | async def test_parameter_validation(helpers):
function test_rm_temp_dir_at_exit (line 962) | async def test_rm_temp_dir_at_exit(helpers):
FILE: bbot/test/test_step_1/test_manager_deduplication.py
function test_manager_deduplication (line 6) | async def test_manager_deduplication(bbot_scanner):
FILE: bbot/test/test_step_1/test_manager_scope_accuracy.py
function bbot_other_httpservers (line 18) | def bbot_other_httpservers():
function test_manager_scope_accuracy (line 45) | async def test_manager_scope_accuracy(bbot_scanner, bbot_httpserver, bbo...
function test_manager_blacklist (line 801) | async def test_manager_blacklist(bbot_scanner, bbot_httpserver, caplog):
function test_manager_scope_tagging (line 828) | async def test_manager_scope_tagging(bbot_scanner):
function test_scope_accuracy_with_special_urls (line 854) | async def test_scope_accuracy_with_special_urls(bbot_scanner, bbot_https...
FILE: bbot/test/test_step_1/test_modules_basic.py
function test_modules_basic_checks (line 12) | async def test_modules_basic_checks(events, httpx_mock):
function test_modules_basic_perhostonly (line 214) | async def test_modules_basic_perhostonly(bbot_scanner):
function test_modules_basic_perdomainonly (line 303) | async def test_modules_basic_perdomainonly(bbot_scanner, monkeypatch):
function test_modules_basic_setup_deps (line 346) | async def test_modules_basic_setup_deps(bbot_scanner):
function test_modules_basic_stats (line 371) | async def test_modules_basic_stats(helpers, events, bbot_scanner, httpx_...
function test_module_loading (line 469) | async def test_module_loading(bbot_scanner):
FILE: bbot/test/test_step_1/test_presets.py
function test_preset_descriptions (line 16) | def test_preset_descriptions():
function test_core (line 25) | def test_core():
function test_preset_yaml (line 70) | def test_preset_yaml(clean_default_config):
function test_preset_cache (line 148) | def test_preset_cache():
function test_preset_scope (line 172) | def test_preset_scope():
function test_preset_logging (line 378) | async def test_preset_logging():
function test_preset_module_resolution (line 478) | def test_preset_module_resolution(clean_default_config):
function test_preset_module_loader (line 572) | async def test_preset_module_loader():
function test_preset_include (line 718) | def test_preset_include():
function test_preset_conditions (line 823) | async def test_preset_conditions():
function test_preset_module_disablement (line 865) | def test_preset_module_disablement(clean_default_config):
function test_preset_override (line 889) | def test_preset_override():
function test_preset_require_exclude (line 971) | def test_preset_require_exclude():
function test_preset_output_dir (line 1062) | async def test_preset_output_dir():
function test_preset_serialization (line 1081) | def test_preset_serialization():
FILE: bbot/test/test_step_1/test_python_api.py
function test_python_api (line 5) | async def test_python_api():
function test_python_api_sync (line 64) | def test_python_api_sync():
function test_python_api_validation (line 84) | def test_python_api_validation():
FILE: bbot/test/test_step_1/test_regexes.py
function test_ip_regexes (line 10) | def test_ip_regexes():
function test_ip_range_regexes (line 116) | def test_ip_range_regexes():
function test_dns_name_regexes (line 164) | def test_dns_name_regexes():
function test_open_port_regexes (line 224) | def test_open_port_regexes():
function test_url_regexes (line 285) | def test_url_regexes():
function test_regex_helper (line 353) | async def test_regex_helper():
FILE: bbot/test/test_step_1/test_scan.py
function test_scan (line 7) | async def test_scan(
function test_task_scan_handle_event_timeout (line 92) | async def test_task_scan_handle_event_timeout(bbot_scanner):
function test_url_extension_handling (line 148) | async def test_url_extension_handling(bbot_scanner):
function test_speed_counter (line 166) | async def test_speed_counter():
function test_python_output_matches_json (line 180) | async def test_python_output_matches_json(bbot_scanner):
function test_huge_target_list (line 205) | async def test_huge_target_list(bbot_scanner, monkeypatch):
function test_exclude_cdn (line 223) | async def test_exclude_cdn(bbot_scanner, monkeypatch):
function test_scan_name (line 282) | async def test_scan_name(bbot_scanner):
FILE: bbot/test/test_step_1/test_scope.py
class TestScopeBaseline (line 5) | class TestScopeBaseline(ModuleTestBase):
method setup_after_prep (line 9) | async def setup_after_prep(self, module_test):
method check (line 14) | def check(self, module_test, events):
class TestScopeBlacklist (line 57) | class TestScopeBlacklist(TestScopeBaseline):
method setup_after_prep (line 60) | async def setup_after_prep(self, module_test):
method check (line 65) | def check(self, module_test, events):
class TestScopeWhitelist (line 71) | class TestScopeWhitelist(TestScopeBlacklist):
method check (line 75) | def check(self, module_test, events):
FILE: bbot/test/test_step_1/test_target.py
function test_target_basic (line 5) | async def test_target_basic(bbot_scanner):
function test_blacklist_regex (line 350) | async def test_blacklist_regex(bbot_scanner, bbot_httpserver):
FILE: bbot/test/test_step_1/test_web.py
function test_web_engine (line 8) | async def test_web_engine(bbot_scanner, bbot_httpserver, httpx_mock):
function test_request_batch_cancellation (line 97) | async def test_request_batch_cancellation(bbot_scanner, bbot_httpserver,...
function test_web_helpers (line 135) | async def test_web_helpers(bbot_scanner, bbot_httpserver, httpx_mock):
function test_web_interactsh (line 282) | async def test_web_interactsh(bbot_scanner, bbot_httpserver):
function test_web_curl (line 345) | async def test_web_curl(bbot_scanner, bbot_httpserver):
function test_web_http_compare (line 380) | async def test_web_http_compare(httpx_mock, bbot_scanner):
function test_http_proxy (line 396) | async def test_http_proxy(bbot_scanner, bbot_httpserver, proxy_server):
function test_http_ssl (line 421) | async def test_http_ssl(bbot_scanner, bbot_httpserver_ssl):
function test_web_cookies (line 441) | async def test_web_cookies(bbot_scanner, httpx_mock):
function test_http_sendcookies (line 484) | async def test_http_sendcookies(bbot_scanner, bbot_httpserver):
function test_api_download_api_key_cycle (line 504) | async def test_api_download_api_key_cycle(bbot_scanner, bbot_httpserver):
FILE: bbot/test/test_step_1/test_web_envelopes.py
function test_web_envelopes (line 4) | async def test_web_envelopes():
function test_web_envelope_pack_value (line 346) | async def test_web_envelope_pack_value():
FILE: bbot/test/test_step_2/module_tests/base.py
class ModuleTestBase (line 14) | class ModuleTestBase:
class ModuleTest (line 26) | class ModuleTest:
method __init__ (line 27) | def __init__(
method set_expect_requests (line 69) | def set_expect_requests(self, expect_args={}, respond_args={}):
method set_expect_requests_handler (line 74) | def set_expect_requests_handler(self, expect_args=None, request_hand...
method mock_dns (line 77) | async def mock_dns(self, mock_data, custom_lookup_fn=None, scan=None):
method mock_interactsh (line 82) | def mock_interactsh(self, name):
method module (line 88) | def module(self):
method module_test (line 92) | async def module_test(
method _execute_scan (line 116) | async def _execute_scan(self, module_test):
method test_module_run (line 121) | async def test_module_run(self, module_test):
method check (line 133) | def check(self, module_test, events):
method name (line 137) | def name(self):
method _scan_name (line 143) | def _scan_name(self):
method modules (line 151) | def modules(self):
method setup_before_prep (line 156) | async def setup_before_prep(self, module_test):
method setup_after_prep (line 159) | async def setup_after_prep(self, module_test):
FILE: bbot/test/test_step_2/module_tests/test_module_affiliates.py
class TestAffiliates (line 4) | class TestAffiliates(ModuleTestBase):
method setup_before_prep (line 8) | async def setup_before_prep(self, module_test):
method check (line 17) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_aggregate.py
class TestAggregate (line 4) | class TestAggregate(ModuleTestBase):
method setup_before_prep (line 7) | async def setup_before_prep(self, module_test):
method check (line 10) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_ajaxpro.py
class TestAjaxpro (line 4) | class TestAjaxpro(ModuleTestBase):
method setup_before_prep (line 12) | async def setup_before_prep(self, module_test):
method check (line 32) | def check(self, module_test, events):
class TestAjaxpro_httpdetect (line 52) | class TestAjaxpro_httpdetect(TestAjaxpro):
method setup_before_prep (line 65) | async def setup_before_prep(self, module_test):
method check (line 71) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_anubisdb.py
class TestAnubisdb (line 4) | class TestAnubisdb(ModuleTestBase):
method setup_after_prep (line 5) | async def setup_after_prep(self, module_test):
method check (line 12) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_apkpure.py
class TestAPKPure (line 6) | class TestAPKPure(ModuleTestBase):
method setup_after_prep (line 11) | async def setup_after_prep(self, module_test):
method check (line 48) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_asn.py
class TestASNBGPView (line 4) | class TestASNBGPView(ModuleTestBase):
method setup_after_prep (line 78) | async def setup_after_prep(self, module_test):
method check (line 87) | def check(self, module_test, events):
class TestASNRipe (line 92) | class TestASNRipe(ModuleTestBase):
method setup_after_prep (line 226) | async def setup_after_prep(self, module_test):
method check (line 237) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_aspnet_bin_exposure.py
class TestAspnetBinExposure (line 5) | class TestAspnetBinExposure(ModuleTestBase):
method setup_before_prep (line 18) | async def setup_before_prep(self, module_test):
method check (line 64) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_asset_inventory.py
class TestAsset_Inventory (line 4) | class TestAsset_Inventory(ModuleTestBase):
method setup_before_prep (line 12) | async def setup_before_prep(self, module_test):
method check (line 34) | def check(self, module_test, events):
class TestAsset_InventoryEmitPrevious (line 50) | class TestAsset_InventoryEmitPrevious(TestAsset_Inventory):
method check (line 54) | def check(self, module_test, events):
class TestAsset_InventoryRecheck (line 70) | class TestAsset_InventoryRecheck(TestAsset_Inventory):
method check (line 77) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_azure_realm.py
class TestAzure_Realm (line 4) | class TestAzure_Realm(ModuleTestBase):
method setup_after_prep (line 22) | async def setup_after_prep(self, module_test):
method check (line 29) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_azure_tenant.py
class TestAzure_Tenant (line 4) | class TestAzure_Tenant(ModuleTestBase):
method setup_after_prep (line 17) | async def setup_after_prep(self, module_test):
method check (line 23) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_baddns.py
class BaseTestBaddns (line 4) | class BaseTestBaddns(ModuleTestBase):
method dispatchWHOIS (line 9) | async def dispatchWHOIS(x):
method select_modules (line 12) | def select_modules(self):
class TestBaddns_cname_nxdomain (line 22) | class TestBaddns_cname_nxdomain(BaseTestBaddns):
method setup_after_prep (line 23) | async def setup_after_prep(self, module_test):
method check (line 33) | def check(self, module_test, events):
class TestBaddns_cname_signature (line 39) | class TestBaddns_cname_signature(BaseTestBaddns):
method setup_after_prep (line 43) | async def setup_after_prep(self, module_test):
method check (line 62) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_baddns_direct.py
class BaseTestBaddns (line 5) | class BaseTestBaddns(ModuleTestBase):
class TestBaddns_direct_cloudflare (line 11) | class TestBaddns_direct_cloudflare(BaseTestBaddns):
method dispatchWHOIS (line 15) | async def dispatchWHOIS(self):
class DummyModule (line 18) | class DummyModule(BaseModule):
method handle_event (line 23) | async def handle_event(self, event):
method setup_after_prep (line 34) | async def setup_after_prep(self, module_test):
method check (line 55) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_baddns_zone.py
class BaseTestBaddns_zone (line 5) | class BaseTestBaddns_zone(ModuleTestBase):
method dispatchWHOIS (line 10) | async def dispatchWHOIS(x):
class TestBaddns_zone_zonetransfer (line 14) | class TestBaddns_zone_zonetransfer(BaseTestBaddns_zone):
method setup_after_prep (line 15) | async def setup_after_prep(self, module_test):
method check (line 38) | def check(self, module_test, events):
class TestBaddns_zone_nsec (line 45) | class TestBaddns_zone_nsec(BaseTestBaddns_zone):
method setup_after_prep (line 46) | async def setup_after_prep(self, module_test):
method check (line 58) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_badsecrets.py
class TestBadSecrets (line 4) | class TestBadSecrets(ModuleTestBase):
method setup_after_prep (line 35) | async def setup_after_prep(self, module_test):
method check (line 73) | def check(self, module_test, events):
class TestBadSecrets_customsecrets (line 126) | class TestBadSecrets_customsecrets(TestBadSecrets):
method check (line 155) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_bevigil.py
class TestBeVigil (line 6) | class TestBeVigil(ModuleTestBase):
method setup_after_prep (line 10) | async def setup_after_prep(self, module_test):
method check (line 26) | def check(self, module_test, events):
class TestBeVigilMultiKey (line 31) | class TestBeVigilMultiKey(TestBeVigil):
method setup_after_prep (line 36) | async def setup_after_prep(self, module_test):
FILE: bbot/test/test_step_2/module_tests/test_module_bucket_amazon.py
class Bucket_Amazon_Base (line 16) | class Bucket_Amazon_Base(ModuleTestBase):
method config_overrides (line 30) | def config_overrides(self):
method module_name (line 34) | def module_name(self):
method modules_overrides (line 38) | def modules_overrides(self):
method url_setup (line 41) | def url_setup(self):
method bucket_setup (line 46) | def bucket_setup(self):
method setup_after_prep (line 53) | async def setup_after_prep(self, module_test):
method check (line 74) | def check(self, module_test, events):
class TestBucket_Amazon (line 130) | class TestBucket_Amazon(Bucket_Amazon_Base):
FILE: bbot/test/test_step_2/module_tests/test_module_bucket_digitalocean.py
class TestBucket_DigitalOcean (line 4) | class TestBucket_DigitalOcean(Bucket_Amazon_Base):
FILE: bbot/test/test_step_2/module_tests/test_module_bucket_file_enum.py
class TestBucket_File_Enum (line 5) | class TestBucket_File_Enum(ModuleTestBase):
method setup_before_prep (line 24) | async def setup_before_prep(self, module_test):
method check (line 40) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_bucket_firebase.py
class TestBucket_Firebase (line 4) | class TestBucket_Firebase(Bucket_Amazon_Base):
method url_setup (line 10) | def url_setup(self):
FILE: bbot/test/test_step_2/module_tests/test_module_bucket_google.py
class TestBucket_Google (line 4) | class TestBucket_Google(Bucket_Amazon_Base):
method bucket_setup (line 17) | def bucket_setup(self):
method url_setup (line 24) | def url_setup(self):
FILE: bbot/test/test_step_2/module_tests/test_module_bucket_microsoft.py
class TestBucket_Microsoft (line 5) | class TestBucket_Microsoft(Bucket_Amazon_Base):
method url_setup (line 13) | def url_setup(self):
class TestBucket_Microsoft_NoDup (line 19) | class TestBucket_Microsoft_NoDup(ModuleTestBase):
method setup_before_prep (line 24) | async def setup_before_prep(self, module_test):
method check (line 36) | def check(self, module_test, events):
method setup_after_prep (line 52) | async def setup_after_prep(self, module_test):
method check (line 57) | def check(self, module_test, events):
class TestBucket_Microsoft_NoDup (line 47) | class TestBucket_Microsoft_NoDup(TestBucket_Microsoft_NoDup):
method setup_before_prep (line 24) | async def setup_before_prep(self, module_test):
method check (line 36) | def check(self, module_test, events):
method setup_after_prep (line 52) | async def setup_after_prep(self, module_test):
method check (line 57) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_bufferoverrun.py
class TestBufferOverrun (line 4) | class TestBufferOverrun(ModuleTestBase):
method setup_before_prep (line 7) | async def setup_before_prep(self, module_test):
method check (line 15) | def check(self, module_test, events):
class TestBufferOverrunCommercial (line 19) | class TestBufferOverrunCommercial(ModuleTestBase):
method setup_before_prep (line 24) | async def setup_before_prep(self, module_test):
method check (line 32) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_builtwith.py
class TestBuiltWith (line 4) | class TestBuiltWith(ModuleTestBase):
method setup_after_prep (line 7) | async def setup_after_prep(self, module_test):
method check (line 108) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_bypass403.py
class TestBypass403 (line 5) | class TestBypass403(ModuleTestBase):
method setup_after_prep (line 9) | async def setup_after_prep(self, module_test):
method check (line 15) | def check(self, module_test, events):
class TestBypass403_collapsethreshold (line 22) | class TestBypass403_collapsethreshold(ModuleTestBase):
method setup_after_prep (line 26) | async def setup_after_prep(self, module_test):
method check (line 66) | def check(self, module_test, events):
class TestBypass403_aspnetcookieless (line 73) | class TestBypass403_aspnetcookieless(ModuleTestBase):
method setup_after_prep (line 77) | async def setup_after_prep(self, module_test):
method check (line 83) | def check(self, module_test, events):
class TestBypass403_waf (line 89) | class TestBypass403_waf(ModuleTestBase):
method setup_after_prep (line 93) | async def setup_after_prep(self, module_test):
method check (line 99) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_c99.py
class TestC99 (line 6) | class TestC99(ModuleTestBase):
method setup_before_prep (line 10) | async def setup_before_prep(self, module_test):
method check (line 27) | def check(self, module_test, events):
class TestC99AbortThreshold1 (line 31) | class TestC99AbortThreshold1(TestC99):
method setup_before_prep (line 34) | async def setup_before_prep(self, module_test):
method check (line 52) | def check(self, module_test, events):
class TestC99AbortThreshold2 (line 69) | class TestC99AbortThreshold2(TestC99AbortThreshold1):
method setup_before_prep (line 72) | async def setup_before_prep(self, module_test):
method check (line 83) | def check(self, module_test, events):
class TestC99AbortThreshold3 (line 104) | class TestC99AbortThreshold3(TestC99AbortThreshold2):
method check (line 107) | def check(self, module_test, events):
class TestC99AbortThreshold4 (line 136) | class TestC99AbortThreshold4(TestC99AbortThreshold3):
method check (line 139) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_censys_dns.py
class TestCensys_DNS (line 4) | class TestCensys_DNS(ModuleTestBase):
method setup_before_prep (line 7) | async def setup_before_prep(self, module_test):
method check (line 80) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_censys_ip.py
class TestCensys_IP (line 4) | class TestCensys_IP(ModuleTestBase):
method setup_before_prep (line 8) | async def setup_before_prep(self, module_test):
method check (line 138) | def check(self, module_test, events):
class TestCensys_IP_InScopeOnly (line 221) | class TestCensys_IP_InScopeOnly(ModuleTestBase):
method setup_before_prep (line 228) | async def setup_before_prep(self, module_test):
method check (line 251) | def check(self, module_test, events):
class TestCensys_IP_OutOfScope (line 258) | class TestCensys_IP_OutOfScope(ModuleTestBase):
method setup_before_prep (line 269) | async def setup_before_prep(self, module_test):
method check (line 292) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_certspotter.py
class TestCertspotter (line 4) | class TestCertspotter(ModuleTestBase):
method setup_after_prep (line 5) | async def setup_after_prep(self, module_test):
method check (line 13) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_chaos.py
class TestChaos (line 4) | class TestChaos(ModuleTestBase):
method setup_before_prep (line 7) | async def setup_before_prep(self, module_test):
method check (line 24) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_cloudcheck.py
class TestCloudCheck (line 6) | class TestCloudCheck(ModuleTestBase):
method setup_after_prep (line 10) | async def setup_after_prep(self, module_test):
method check (line 63) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_code_repository.py
class TestCodeRepository (line 4) | class TestCodeRepository(ModuleTestBase):
method setup_after_prep (line 8) | async def setup_after_prep(self, module_test):
method check (line 23) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_credshed.py
class TestCredshed (line 54) | class TestCredshed(ModuleTestBase):
method setup_before_prep (line 59) | async def setup_before_prep(self, module_test):
method check (line 71) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_crt.py
class TestCRT (line 4) | class TestCRT(ModuleTestBase):
method setup_after_prep (line 5) | async def setup_after_prep(self, module_test):
method check (line 13) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_crt_db.py
class TestCRT_DB (line 4) | class TestCRT_DB(ModuleTestBase):
method setup_after_prep (line 5) | async def setup_after_prep(self, module_test):
method check (line 21) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_csv.py
class TestCSV (line 4) | class TestCSV(ModuleTestBase):
method setup_after_prep (line 5) | async def setup_after_prep(self, module_test):
method check (line 8) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_dehashed.py
class TestDehashed (line 4) | class TestDehashed(ModuleTestBase):
method setup_before_prep (line 11) | async def setup_before_prep(self, module_test):
method check (line 50) | def check(self, module_test, events):
class TestDehashedBadEmail (line 97) | class TestDehashedBadEmail(TestDehashed):
method setup_before_prep (line 98) | async def setup_before_prep(self, module_test):
method check (line 116) | def check(self, module_test, events):
class TestDehashedHTTPError (line 121) | class TestDehashedHTTPError(TestDehashed):
method setup_before_prep (line 122) | async def setup_before_prep(self, module_test):
method check (line 130) | def check(self, module_test, events):
class TestDehashedTooManyResults (line 138) | class TestDehashedTooManyResults(TestDehashed):
method setup_before_prep (line 139) | async def setup_before_prep(self, module_test):
method check (line 157) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_digitorus.py
class TestDigitorus (line 4) | class TestDigitorus(ModuleTestBase):
method setup_after_prep (line 12) | async def setup_after_prep(self, module_test):
method check (line 18) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_discord.py
class TestDiscord (line 6) | class TestDiscord(ModuleTestBase):
method custom_setup (line 13) | def custom_setup(self, module_test):
method setup_after_prep (line 24) | async def setup_after_prep(self, module_test):
method check (line 36) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_dnsbimi.py
class TestDnsbimi (line 9) | class TestDnsbimi(ModuleTestBase):
method setup_after_prep (line 17) | async def setup_after_prep(self, module_test):
method check (line 56) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_dnsbrute.py
class TestDnsbrute (line 4) | class TestDnsbrute(ModuleTestBase):
method setup_after_prep (line 9) | async def setup_after_prep(self, module_test):
method check (line 93) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_dnsbrute_mutations.py
class TestDnsbrute_mutations (line 4) | class TestDnsbrute_mutations(ModuleTestBase):
method setup_after_prep (line 13) | async def setup_after_prep(self, module_test):
method check (line 48) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_dnscaa.py
class TestDNSCAA (line 4) | class TestDNSCAA(ModuleTestBase):
method setup_after_prep (line 13) | async def setup_after_prep(self, module_test):
method check (line 41) | def check(self, module_test, events):
class TestDNSCAAInScopeFalse (line 58) | class TestDNSCAAInScopeFalse(TestDNSCAA):
method check (line 61) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_dnscommonsrv.py
class TestDNSCommonSRV (line 4) | class TestDNSCommonSRV(ModuleTestBase):
method setup_after_prep (line 10) | async def setup_after_prep(self, module_test):
method check (line 57) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_dnsdumpster.py
class TestDNSDumpster (line 4) | class TestDNSDumpster(ModuleTestBase):
method setup_after_prep (line 5) | async def setup_after_prep(self, module_test):
method check (line 15) | def check(self, module_test, events):
FILE: bbot/test/test_step_2/module_tests/test_module_dnsresolve.py
class TestDNSREsolve (line 4) | class TestDNSREsolve(ModuleTestB
Condensed preview — 543 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (5,152K chars).
[
{
"path": ".gitattributes",
"chars": 120,
"preview": "* text=auto\n\n*.py text eol=lf\n*.conf text eol=lf\n*.txt text eol=lf\n*.json text eol=lf\n*.md text eol=lf\n*.sh text eol=lf\n"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 807,
"preview": "---\nname: Bug Report\nabout: Create a report to help us improve\ntitle: \"\"\nlabels: bug\nassignees: \"\"\n---\n\n**Describe the b"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 195,
"preview": "---\nname: Feature Request\nabout: Request a new feature\ntitle: \"\"\nlabels: enhancement\nassignees: \"\"\n---\n\n**Description**\n"
},
{
"path": ".github/dependabot.yml",
"chars": 414,
"preview": "version: 2\nupdates:\n - package-ecosystem: \"pip\"\n directory: \"/\"\n schedule:\n interval: \"weekly\"\n target-br"
},
{
"path": ".github/workflows/benchmark.yml",
"chars": 6342,
"preview": "name: Performance Benchmarks\n\non:\n pull_request:\n paths:\n - 'bbot/**/*.py'\n - 'pyproject.toml'\n - '.g"
},
{
"path": ".github/workflows/codeql.yml",
"chars": 4626,
"preview": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# Y"
},
{
"path": ".github/workflows/distro_tests.yml",
"chars": 3165,
"preview": "name: Tests (Linux Distros)\non:\n pull_request:\n\nconcurrency:\n group: ${{ github.workflow }}-${{ github.event_name }}-$"
},
{
"path": ".github/workflows/docs_updater.yml",
"chars": 1029,
"preview": "name: Daily Docs Update\n\non:\n schedule:\n - cron: '30 2 * * *' # Runs daily at 2:30 AM UTC, a less congested time\n "
},
{
"path": ".github/workflows/tests.yml",
"chars": 9893,
"preview": "name: Tests\non:\n push:\n branches:\n - stable\n - dev\n pull_request:\n\nconcurrency:\n group: ${{ github.workf"
},
{
"path": ".github/workflows/version_updater.yml",
"chars": 4723,
"preview": "name: Version Updater\non:\n schedule:\n # Runs at 00:00 every day\n - cron: '0 0 * * *'\n workflow_dispatch: # Adds "
},
{
"path": ".gitignore",
"chars": 39,
"preview": "__pycache__/\n.coverage*\n/data/\n/neo4j/\n"
},
{
"path": ".gitmodules",
"chars": 164,
"preview": "[submodule \"bbot/modules/playground\"]\n path = bbot/modules/playground\n url = https://github.com/blacklanternsecuri"
},
{
"path": ".pre-commit-config.yaml",
"chars": 1415,
"preview": "# Learn more about this config here: https://pre-commit.com/\n\n# To enable these pre-commit hooks run:\n# `pipx install pr"
},
{
"path": "Dockerfile",
"chars": 267,
"preview": "FROM python:3.11-slim\n\nENV LANG=C.UTF-8\nENV LC_ALL=C.UTF-8\nENV PIP_NO_CACHE_DIR=off\n\nWORKDIR /usr/src/bbot\n\nRUN apt-get "
},
{
"path": "Dockerfile.full",
"chars": 296,
"preview": "FROM python:3.11-slim\n\nENV LANG=C.UTF-8\nENV LC_ALL=C.UTF-8\nENV PIP_NO_CACHE_DIR=off\n\nWORKDIR /usr/src/bbot\n\nRUN apt-get "
},
{
"path": "LICENSE",
"chars": 34523,
"preview": " GNU AFFERO GENERAL PUBLIC LICENSE\n Version 3, 19 November 2007\n\n Copyright (C)"
},
{
"path": "README.md",
"chars": 15753,
"preview": "[](https://github.com/bla"
},
{
"path": "bbot/__init__.py",
"chars": 156,
"preview": "# version placeholder (replaced by poetry-dynamic-versioning)\n__version__ = \"v0.0.0\"\n\nfrom .scanner import Scanner, Pres"
},
{
"path": "bbot/cli.py",
"chars": 12796,
"preview": "#!/usr/bin/env python3\n\nimport io\nimport sys\nimport logging\nimport multiprocessing\nfrom bbot.errors import *\nfrom bbot i"
},
{
"path": "bbot/core/__init__.py",
"chars": 46,
"preview": "from .core import BBOTCore\n\nCORE = BBOTCore()\n"
},
{
"path": "bbot/core/config/__init__.py",
"chars": 342,
"preview": "import sys\nimport multiprocessing as mp\n\ntry:\n mp.set_start_method(\"spawn\")\nexcept Exception:\n start_method = mp.g"
},
{
"path": "bbot/core/config/files.py",
"chars": 1412,
"preview": "import sys\nfrom pathlib import Path\nfrom omegaconf import OmegaConf\n\nfrom ...logger import log_to_stderr\nfrom ...errors "
},
{
"path": "bbot/core/config/logger.py",
"chars": 10226,
"preview": "import os\nimport sys\nimport atexit\nimport logging\nfrom copy import copy\nimport multiprocessing\nimport logging.handlers\nf"
},
{
"path": "bbot/core/core.py",
"chars": 7065,
"preview": "import os\nimport logging\nfrom copy import copy\nfrom pathlib import Path\nfrom contextlib import suppress\nfrom omegaconf i"
},
{
"path": "bbot/core/engine.py",
"chars": 29362,
"preview": "import os\nimport sys\nimport zmq\nimport pickle\nimport asyncio\nimport inspect\nimport logging\nimport tempfile\nimport traceb"
},
{
"path": "bbot/core/event/__init__.py",
"chars": 143,
"preview": "from .base import make_event, update_event, is_event, event_from_json\n\n__all__ = [\"make_event\", \"update_event\", \"is_even"
},
{
"path": "bbot/core/event/base.py",
"chars": 67573,
"preview": "import io\nimport re\nimport uuid\nimport json\nimport base64\nimport logging\nimport tarfile\nimport datetime\nimport ipaddress"
},
{
"path": "bbot/core/event/helpers.py",
"chars": 6507,
"preview": "import ipaddress\nimport regex as re\nfrom functools import cached_property\nfrom bbot.errors import ValidationError\nfrom b"
},
{
"path": "bbot/core/flags.py",
"chars": 1336,
"preview": "flag_descriptions = {\n \"active\": \"Makes active connections to target systems\",\n \"affiliates\": \"Discovers affiliate"
},
{
"path": "bbot/core/helpers/__init__.py",
"chars": 111,
"preview": "from .url import *\nfrom .misc import *\nfrom . import regexes as regexes\nfrom . import validators as validators\n"
},
{
"path": "bbot/core/helpers/async_helpers.py",
"chars": 4399,
"preview": "import time\nimport uuid\nimport random\nimport asyncio\nimport logging\nimport functools\nfrom contextlib import suppress\nfro"
},
{
"path": "bbot/core/helpers/bloom.py",
"chars": 2605,
"preview": "import os\nimport mmh3\nimport mmap\nimport xxhash\n\n\nclass BloomFilter:\n \"\"\"\n Simple bloom filter implementation capa"
},
{
"path": "bbot/core/helpers/cache.py",
"chars": 1537,
"preview": "import os\nimport time\nimport logging\n\nfrom .misc import sha1\n\nlog = logging.getLogger(\"bbot.core.helpers.cache\")\n\n\ndef c"
},
{
"path": "bbot/core/helpers/command.py",
"chars": 12938,
"preview": "import os\nimport asyncio\nimport logging\nimport traceback\nfrom signal import SIGINT\nfrom subprocess import CompletedProce"
},
{
"path": "bbot/core/helpers/depsinstaller/__init__.py",
"chars": 66,
"preview": "from .installer import DepsInstaller\n\n__all__ = [\"DepsInstaller\"]\n"
},
{
"path": "bbot/core/helpers/depsinstaller/installer.py",
"chars": 22969,
"preview": "import os\nimport sys\nimport stat\nimport json\nimport mmh3\nimport orjson\nimport shutil\nimport getpass\nimport logging\nfrom "
},
{
"path": "bbot/core/helpers/depsinstaller/sudo_askpass.py",
"chars": 1276,
"preview": "#!/usr/bin/env python3\nimport os\nimport sys\nfrom pathlib import Path\nfrom Crypto.Cipher import AES\nfrom Crypto.Util.Padd"
},
{
"path": "bbot/core/helpers/diff.py",
"chars": 10841,
"preview": "import logging\nimport xmltodict\nfrom deepdiff import DeepDiff\nfrom contextlib import suppress\nfrom xml.parsers.expat imp"
},
{
"path": "bbot/core/helpers/dns/__init__.py",
"chars": 35,
"preview": "from .dns import DNSHelper # noqa\n"
},
{
"path": "bbot/core/helpers/dns/brute.py",
"chars": 7143,
"preview": "import json\nimport random\nimport asyncio\nimport logging\nimport subprocess\n\n\nclass DNSBrute:\n \"\"\"\n Helper for DNS b"
},
{
"path": "bbot/core/helpers/dns/dns.py",
"chars": 8308,
"preview": "import dns\nimport logging\nimport dns.exception\nimport dns.asyncresolver\nfrom cachetools import LFUCache\nfrom radixtarget"
},
{
"path": "bbot/core/helpers/dns/engine.py",
"chars": 28704,
"preview": "import os\nimport dns\nimport time\nimport asyncio\nimport logging\nimport traceback\nfrom cachetools import LRUCache\nfrom con"
},
{
"path": "bbot/core/helpers/dns/helpers.py",
"chars": 8505,
"preview": "import logging\n\nfrom bbot.core.helpers.regexes import dns_name_extraction_regex\nfrom bbot.core.helpers.misc import clean"
},
{
"path": "bbot/core/helpers/dns/mock.py",
"chars": 2588,
"preview": "import dns\nimport logging\n\nlog = logging.getLogger(\"bbot.core.helpers.dns.mock\")\n\n\nclass MockResolver:\n def __init__("
},
{
"path": "bbot/core/helpers/files.py",
"chars": 5811,
"preview": "import os\nimport logging\nimport traceback\nfrom contextlib import suppress\n\nfrom .misc import rm_at_exit\n\n\nlog = logging."
},
{
"path": "bbot/core/helpers/git.py",
"chars": 757,
"preview": "from pathlib import Path\n\n\ndef sanitize_git_repo(repo_folder: Path):\n # sanitizing the git config is infeasible since"
},
{
"path": "bbot/core/helpers/helper.py",
"chars": 8582,
"preview": "import os\nimport logging\nfrom pathlib import Path\nimport multiprocessing as mp\nfrom functools import partial\nfrom concur"
},
{
"path": "bbot/core/helpers/interactsh.py",
"chars": 12705,
"preview": "# based on https://github.com/ElSicarius/interactsh-python/blob/main/sources/interactsh.py\nimport json\nimport base64\nimp"
},
{
"path": "bbot/core/helpers/libmagic.py",
"chars": 3460,
"preview": "import puremagic\n\n\ndef get_magic_info(file):\n magic_detections = puremagic.magic_file(file)\n if magic_detections:\n"
},
{
"path": "bbot/core/helpers/misc.py",
"chars": 89329,
"preview": "import os\nimport sys\nimport copy\nimport json\nimport math\nimport random\nimport string\nimport asyncio\nimport logging\nimpor"
},
{
"path": "bbot/core/helpers/names_generator.py",
"chars": 10594,
"preview": "import random\n\nadjectives = [\n \"abnormal\",\n \"accidental\",\n \"acoustic\",\n \"acrophobic\",\n \"adorable\",\n \"a"
},
{
"path": "bbot/core/helpers/ntlm.py",
"chars": 2516,
"preview": "# Stolen from https://github.com/blacklanternsecurity/TREVORspray who stole it from https://github.com/byt3bl33d3r/Spray"
},
{
"path": "bbot/core/helpers/process.py",
"chars": 1702,
"preview": "import logging\nimport traceback\nimport threading\nfrom multiprocessing.context import SpawnProcess\n\nfrom .misc import in_"
},
{
"path": "bbot/core/helpers/ratelimiter.py",
"chars": 2115,
"preview": "import time\nimport asyncio\nimport logging\n\nlog = logging.getLogger(\"bbot.helpers.ratelimiter\")\n\n\nclass RateLimiter:\n "
},
{
"path": "bbot/core/helpers/regex.py",
"chars": 4572,
"preview": "import asyncio\nimport regex as re\nfrom . import misc\n\n\nclass RegexHelper:\n \"\"\"\n Class for misc CPU-intensive regex"
},
{
"path": "bbot/core/helpers/regexes.py",
"chars": 8737,
"preview": "import regex as re\nfrom collections import OrderedDict\n\n# for extracting words from strings\nword_regexes = [\n re.comp"
},
{
"path": "bbot/core/helpers/url.py",
"chars": 6352,
"preview": "import uuid\nimport logging\nfrom contextlib import suppress\nfrom urllib.parse import urlparse, parse_qs, urlencode, Parse"
},
{
"path": "bbot/core/helpers/validators.py",
"chars": 9711,
"preview": "import logging\nimport ipaddress\nfrom typing import Union\nfrom functools import wraps\nfrom contextlib import suppress\n\nfr"
},
{
"path": "bbot/core/helpers/web/__init__.py",
"chars": 35,
"preview": "from .web import WebHelper # noqa\n"
},
{
"path": "bbot/core/helpers/web/client.py",
"chars": 4082,
"preview": "import httpx\nimport logging\nfrom httpx._models import Cookies\n\nlog = logging.getLogger(\"bbot.core.helpers.web.client\")\n\n"
},
{
"path": "bbot/core/helpers/web/engine.py",
"chars": 9221,
"preview": "import ssl\nimport anyio\nimport httpx\nimport asyncio\nimport logging\nimport traceback\nfrom socksio.exceptions import SOCKS"
},
{
"path": "bbot/core/helpers/web/envelopes.py",
"chars": 11247,
"preview": "import json\nimport base64\nimport binascii\nimport xmltodict\nfrom contextlib import suppress\nfrom urllib.parse import unqu"
},
{
"path": "bbot/core/helpers/web/ssl_context.py",
"chars": 356,
"preview": "import ssl\n\nssl_context_noverify = ssl.create_default_context()\nssl_context_noverify.check_hostname = False\nssl_context_"
},
{
"path": "bbot/core/helpers/web/web.py",
"chars": 24088,
"preview": "import logging\nimport warnings\nfrom pathlib import Path\nfrom bs4 import BeautifulSoup\n\nfrom bbot.core.engine import Engi"
},
{
"path": "bbot/core/helpers/wordcloud.py",
"chars": 19809,
"preview": "import re\nimport csv\nimport string\nimport logging\nimport wordninja\nfrom pathlib import Path\nfrom contextlib import suppr"
},
{
"path": "bbot/core/helpers/yara_helper.py",
"chars": 1592,
"preview": "import yara\n\n\nclass YaraHelper:\n def __init__(self, parent_helper):\n self.parent_helper = parent_helper\n\n d"
},
{
"path": "bbot/core/modules.py",
"chars": 30221,
"preview": "import re\nimport ast\nimport sys\nimport atexit\nimport pickle\nimport logging\nimport importlib\nimport omegaconf\nimport trac"
},
{
"path": "bbot/core/multiprocess.py",
"chars": 1771,
"preview": "import os\nimport atexit\nfrom contextlib import suppress\n\n\nclass SharedInterpreterState:\n \"\"\"\n A class to track the"
},
{
"path": "bbot/core/shared_deps.py",
"chars": 9505,
"preview": "DEP_FFUF = [\n {\n \"name\": \"Download ffuf\",\n \"unarchive\": {\n \"src\": \"https://github.com/ffuf/f"
},
{
"path": "bbot/db/sql/models.py",
"chars": 4884,
"preview": "# This file contains SQLModel (Pydantic + SQLAlchemy) models for BBOT events, scans, and targets.\n# Used by the SQL outp"
},
{
"path": "bbot/defaults.yml",
"chars": 7961,
"preview": "### BASIC OPTIONS ###\n\n# BBOT working directory\nhome: ~/.bbot\n# How many scan results to keep before cleaning up the old"
},
{
"path": "bbot/errors.py",
"chars": 953,
"preview": "class BBOTError(Exception):\n pass\n\n\nclass ScanError(BBOTError):\n pass\n\n\nclass ValidationError(BBOTError):\n pass"
},
{
"path": "bbot/logger.py",
"chars": 2745,
"preview": "import os\nimport sys\nimport logging.handlers\n\nloglevel_mapping = {\n \"DEBUG\": \"DBUG\",\n \"TRACE\": \"TRCE\",\n \"VERBOS"
},
{
"path": "bbot/modules/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "bbot/modules/ajaxpro.py",
"chars": 3764,
"preview": "import regex as re\nfrom urllib.parse import urlparse\nfrom bbot.modules.base import BaseModule\n\n\nclass ajaxpro(BaseModule"
},
{
"path": "bbot/modules/anubisdb.py",
"chars": 1916,
"preview": "from bbot.modules.templates.subdomain_enum import subdomain_enum\n\n\nclass anubisdb(subdomain_enum):\n flags = [\"subdoma"
},
{
"path": "bbot/modules/apkpure.py",
"chars": 2567,
"preview": "import re\nfrom pathlib import Path\nfrom bbot.modules.base import BaseModule\n\n\nclass apkpure(BaseModule):\n watched_eve"
},
{
"path": "bbot/modules/aspnet_bin_exposure.py",
"chars": 3737,
"preview": "from bbot.modules.base import BaseModule\n\n\nclass aspnet_bin_exposure(BaseModule):\n watched_events = [\"URL\"]\n produ"
},
{
"path": "bbot/modules/azure_realm.py",
"chars": 1612,
"preview": "from .base import BaseModule\n\n\nclass azure_realm(BaseModule):\n watched_events = [\"DNS_NAME\"]\n produced_events = [\""
},
{
"path": "bbot/modules/azure_tenant.py",
"chars": 3505,
"preview": "from bbot.modules.base import BaseModule\n\n\nclass azure_tenant(BaseModule):\n watched_events = [\"DNS_NAME\"]\n produce"
},
{
"path": "bbot/modules/baddns.py",
"chars": 6694,
"preview": "from baddns.base import get_all_modules\nfrom baddns.lib.loader import load_signatures\nfrom .base import BaseModule\n\nimpo"
},
{
"path": "bbot/modules/baddns_direct.py",
"chars": 3820,
"preview": "from baddns.base import get_all_modules\nfrom baddns.lib.loader import load_signatures\nfrom .base import BaseModule\n\nimpo"
},
{
"path": "bbot/modules/baddns_zone.py",
"chars": 1036,
"preview": "from .baddns import baddns as baddns_module\n\n\nclass baddns_zone(baddns_module):\n watched_events = [\"DNS_NAME\"]\n pr"
},
{
"path": "bbot/modules/badsecrets.py",
"chars": 5110,
"preview": "import multiprocessing\nfrom pathlib import Path\nfrom .base import BaseModule\nfrom badsecrets.base import carve_all_modul"
},
{
"path": "bbot/modules/base.py",
"chars": 82272,
"preview": "import asyncio\nimport logging\nimport traceback\nfrom sys import exc_info\nfrom contextlib import suppress\n\nfrom ..core.hel"
},
{
"path": "bbot/modules/bevigil.py",
"chars": 2862,
"preview": "from bbot.modules.templates.subdomain_enum import subdomain_enum_apikey\n\n\nclass bevigil(subdomain_enum_apikey):\n \"\"\"\n"
},
{
"path": "bbot/modules/bucket_amazon.py",
"chars": 737,
"preview": "from bbot.modules.templates.bucket import bucket_template\n\n\nclass bucket_amazon(bucket_template):\n watched_events = ["
},
{
"path": "bbot/modules/bucket_digitalocean.py",
"chars": 873,
"preview": "from bbot.modules.templates.bucket import bucket_template\n\n\nclass bucket_digitalocean(bucket_template):\n watched_even"
},
{
"path": "bbot/modules/bucket_file_enum.py",
"chars": 2388,
"preview": "from bbot.modules.base import BaseModule\nimport xml.etree.ElementTree as ET\n\n\nclass bucket_file_enum(BaseModule):\n \"\""
},
{
"path": "bbot/modules/bucket_firebase.py",
"chars": 1387,
"preview": "from bbot.modules.templates.bucket import bucket_template\n\n\nclass bucket_firebase(bucket_template):\n watched_events ="
},
{
"path": "bbot/modules/bucket_google.py",
"chars": 2552,
"preview": "from bbot.modules.templates.bucket import bucket_template\n\n\nclass bucket_google(bucket_template):\n \"\"\"\n Adapted fr"
},
{
"path": "bbot/modules/bucket_microsoft.py",
"chars": 1299,
"preview": "from bbot.modules.templates.bucket import bucket_template\n\n\nclass bucket_microsoft(bucket_template):\n watched_events "
},
{
"path": "bbot/modules/bufferoverrun.py",
"chars": 1824,
"preview": "from bbot.modules.templates.subdomain_enum import subdomain_enum_apikey\n\n\nclass BufferOverrun(subdomain_enum_apikey):\n "
},
{
"path": "bbot/modules/builtwith.py",
"chars": 5609,
"preview": "############################################################\n# "
},
{
"path": "bbot/modules/bypass403.py",
"chars": 6855,
"preview": "from bbot.errors import HttpCompareError\nfrom bbot.modules.base import BaseModule\n\n\"\"\"\nPort of https://github.com/iamj0k"
},
{
"path": "bbot/modules/c99.py",
"chars": 1473,
"preview": "from bbot.modules.templates.subdomain_enum import subdomain_enum_apikey\n\n\nclass c99(subdomain_enum_apikey):\n watched_"
},
{
"path": "bbot/modules/censys_dns.py",
"chars": 2727,
"preview": "from bbot.modules.templates.censys import censys\n\n\nclass censys_dns(censys):\n \"\"\"\n Query the Censys certificates A"
},
{
"path": "bbot/modules/censys_ip.py",
"chars": 7224,
"preview": "from bbot.modules.templates.censys import censys\n\n\nclass censys_ip(censys):\n \"\"\"\n Query the Censys /v2/hosts/{ip} "
},
{
"path": "bbot/modules/certspotter.py",
"chars": 905,
"preview": "from bbot.modules.templates.subdomain_enum import subdomain_enum\n\n\nclass certspotter(subdomain_enum):\n watched_events"
},
{
"path": "bbot/modules/chaos.py",
"chars": 1573,
"preview": "from bbot.modules.templates.subdomain_enum import subdomain_enum_apikey\n\n\nclass chaos(subdomain_enum_apikey):\n watche"
},
{
"path": "bbot/modules/code_repository.py",
"chars": 2078,
"preview": "import re\nfrom bbot.modules.base import BaseModule\n\n\nclass code_repository(BaseModule):\n watched_events = [\"URL_UNVER"
},
{
"path": "bbot/modules/credshed.py",
"chars": 4203,
"preview": "from contextlib import suppress\n\nfrom bbot.modules.templates.subdomain_enum import subdomain_enum\n\n\nclass credshed(subdo"
},
{
"path": "bbot/modules/crt.py",
"chars": 1369,
"preview": "from bbot.modules.templates.subdomain_enum import subdomain_enum\n\n\nclass crt(subdomain_enum):\n flags = [\"subdomain-en"
},
{
"path": "bbot/modules/crt_db.py",
"chars": 2198,
"preview": "import time\nimport asyncpg\n\nfrom bbot.modules.templates.subdomain_enum import subdomain_enum\n\n\nclass crt_db(subdomain_en"
},
{
"path": "bbot/modules/deadly/legba.py",
"chars": 9782,
"preview": "import json\nfrom pathlib import Path\nfrom bbot.errors import WordlistError\nfrom bbot.modules.base import BaseModule\n\n# k"
},
{
"path": "bbot/modules/dehashed.py",
"chars": 5155,
"preview": "from contextlib import suppress\n\nfrom bbot.modules.templates.subdomain_enum import subdomain_enum\n\n\nclass dehashed(subdo"
},
{
"path": "bbot/modules/digitorus.py",
"chars": 1027,
"preview": "import re\n\nfrom bbot.modules.templates.subdomain_enum import subdomain_enum\n\n\nclass digitorus(subdomain_enum):\n flags"
},
{
"path": "bbot/modules/dnsbimi.py",
"chars": 6931,
"preview": "# bimi.py\n#\n# Checks for and parses common BIMI DNS TXT records, e.g. default._bimi.target.domain\n#\n# Example TXT record"
},
{
"path": "bbot/modules/dnsbrute.py",
"chars": 2607,
"preview": "from bbot.modules.templates.subdomain_enum import subdomain_enum\n\n\nclass dnsbrute(subdomain_enum):\n flags = [\"subdoma"
},
{
"path": "bbot/modules/dnsbrute_mutations.py",
"chars": 7242,
"preview": "import time\n\nfrom bbot.modules.base import BaseModule\n\n\nclass dnsbrute_mutations(BaseModule):\n flags = [\"subdomain-en"
},
{
"path": "bbot/modules/dnscaa.py",
"chars": 5014,
"preview": "# dnscaa.py\n#\n# Checks for and parses CAA DNS TXT records for IODEF reporting destination email addresses and/or URL's.\n"
},
{
"path": "bbot/modules/dnscommonsrv.py",
"chars": 1568,
"preview": "from bbot.core.helpers.dns.helpers import common_srvs\nfrom bbot.modules.templates.subdomain_enum import subdomain_enum\n\n"
},
{
"path": "bbot/modules/dnsdumpster.py",
"chars": 2775,
"preview": "import json\n\nfrom bbot.modules.templates.subdomain_enum import subdomain_enum\n\n\nclass dnsdumpster(subdomain_enum):\n w"
},
{
"path": "bbot/modules/dnstlsrpt.py",
"chars": 6238,
"preview": "# dnstlsrpt.py\n#\n# Checks for and parses common TLS-RPT TXT records, e.g. _smtp._tls.target.domain\n#\n# TLS-RPT policies "
},
{
"path": "bbot/modules/docker_pull.py",
"chars": 9201,
"preview": "import io\nimport json\nimport tarfile\nfrom pathlib import Path\nfrom bbot.modules.base import BaseModule\n\n\nclass docker_pu"
},
{
"path": "bbot/modules/dockerhub.py",
"chars": 3493,
"preview": "from bbot.modules.base import BaseModule\n\n\nclass dockerhub(BaseModule):\n watched_events = [\"SOCIAL\", \"ORG_STUB\"]\n "
},
{
"path": "bbot/modules/dotnetnuke.py",
"chars": 10537,
"preview": "from bbot.errors import InteractshError\nfrom bbot.modules.base import BaseModule\n\n\nclass dotnetnuke(BaseModule):\n DNN"
},
{
"path": "bbot/modules/emailformat.py",
"chars": 1518,
"preview": "from bbot.modules.base import BaseModule\n\n\nclass emailformat(BaseModule):\n watched_events = [\"DNS_NAME\"]\n produced"
},
{
"path": "bbot/modules/extractous.py",
"chars": 4648,
"preview": "from extractous import Extractor\n\nfrom bbot.modules.base import BaseModule\n\n\nclass extractous(BaseModule):\n watched_e"
},
{
"path": "bbot/modules/ffuf.py",
"chars": 15033,
"preview": "from bbot.modules.base import BaseModule\n\nimport random\nimport string\nimport json\nimport base64\n\n\nclass ffuf(BaseModule)"
},
{
"path": "bbot/modules/ffuf_shortnames.py",
"chars": 18790,
"preview": "import pickle\nimport re\nimport random\nimport string\n\nfrom bbot.modules.ffuf import ffuf\n\n\nclass ffuf_shortnames(ffuf):\n "
},
{
"path": "bbot/modules/filedownload.py",
"chars": 8968,
"preview": "import json\nfrom pathlib import Path\n\nfrom bbot.modules.base import BaseModule\n\n\nclass filedownload(BaseModule):\n \"\"\""
},
{
"path": "bbot/modules/fingerprintx.py",
"chars": 3276,
"preview": "import json\nimport subprocess\nfrom bbot.modules.base import BaseModule\n\n\nclass fingerprintx(BaseModule):\n watched_eve"
},
{
"path": "bbot/modules/fullhunt.py",
"chars": 1336,
"preview": "from bbot.modules.templates.subdomain_enum import subdomain_enum_apikey\n\n\nclass fullhunt(subdomain_enum_apikey):\n wat"
},
{
"path": "bbot/modules/generic_ssrf.py",
"chars": 9196,
"preview": "from bbot.errors import InteractshError\nfrom bbot.modules.base import BaseModule\n\n\nssrf_params = [\n \"Dest\",\n \"Redi"
},
{
"path": "bbot/modules/git.py",
"chars": 1723,
"preview": "import re\n\nfrom bbot.modules.base import BaseModule\n\n\nclass git(BaseModule):\n watched_events = [\"URL\"]\n produced_e"
},
{
"path": "bbot/modules/git_clone.py",
"chars": 3776,
"preview": "from pathlib import Path\nfrom subprocess import CalledProcessError\nfrom bbot.modules.templates.github import github\n\n\ncl"
},
{
"path": "bbot/modules/gitdumper.py",
"chars": 11600,
"preview": "import asyncio\nfrom pathlib import Path\nfrom subprocess import CalledProcessError\nfrom bbot.modules.base import BaseModu"
},
{
"path": "bbot/modules/github_codesearch.py",
"chars": 3641,
"preview": "from bbot.modules.templates.github import github\nfrom bbot.modules.templates.subdomain_enum import subdomain_enum\n\n\nclas"
},
{
"path": "bbot/modules/github_org.py",
"chars": 8938,
"preview": "from bbot.modules.templates.github import github\n\n\nclass github_org(github):\n watched_events = [\"ORG_STUB\", \"SOCIAL\"]"
},
{
"path": "bbot/modules/github_usersearch.py",
"chars": 3407,
"preview": "from bbot.modules.templates.github import github\nfrom bbot.modules.templates.subdomain_enum import subdomain_enum\n\n\nclas"
},
{
"path": "bbot/modules/github_workflows.py",
"chars": 10237,
"preview": "import zipfile\nimport fnmatch\nfrom pathlib import Path\n\nfrom bbot.modules.templates.github import github\n\n\nclass github_"
},
{
"path": "bbot/modules/gitlab_com.py",
"chars": 1051,
"preview": "from bbot.modules.templates.gitlab import GitLabBaseModule\n\n\nclass gitlab_com(GitLabBaseModule):\n watched_events = [\""
},
{
"path": "bbot/modules/gitlab_onprem.py",
"chars": 3622,
"preview": "from bbot.modules.templates.gitlab import GitLabBaseModule\n\n\nclass gitlab_onprem(GitLabBaseModule):\n watched_events ="
},
{
"path": "bbot/modules/google_playstore.py",
"chars": 3701,
"preview": "from bbot.modules.base import BaseModule\n\n\nclass google_playstore(BaseModule):\n watched_events = [\"ORG_STUB\", \"CODE_R"
},
{
"path": "bbot/modules/gowitness.py",
"chars": 13151,
"preview": "import os\nimport asyncio\nimport aiosqlite\nimport multiprocessing\nimport platform\nfrom pathlib import Path\nfrom contextli"
},
{
"path": "bbot/modules/graphql_introspection.py",
"chars": 4239,
"preview": "import json\nfrom pathlib import Path\nfrom bbot.modules.base import BaseModule\n\n\nclass graphql_introspection(BaseModule):"
},
{
"path": "bbot/modules/hackertarget.py",
"chars": 1029,
"preview": "from bbot.modules.templates.subdomain_enum import subdomain_enum\n\n\nclass hackertarget(subdomain_enum):\n watched_event"
},
{
"path": "bbot/modules/host_header.py",
"chars": 7686,
"preview": "from bbot.errors import InteractshError\nfrom bbot.modules.base import BaseModule\n\n\nclass host_header(BaseModule):\n wa"
},
{
"path": "bbot/modules/httpx.py",
"chars": 8184,
"preview": "import re\nimport orjson\nimport tempfile\nimport subprocess\nfrom pathlib import Path\nfrom http.cookies import SimpleCookie"
},
{
"path": "bbot/modules/hunt.py",
"chars": 6607,
"preview": "# adapted from https://github.com/bugcrowd/HUNT\n\nfrom bbot.modules.base import BaseModule\n\nhunt_param_dict = {\n \"Comm"
},
{
"path": "bbot/modules/hunterio.py",
"chars": 2644,
"preview": "from bbot.modules.templates.subdomain_enum import subdomain_enum_apikey\n\n\nclass hunterio(subdomain_enum_apikey):\n wat"
},
{
"path": "bbot/modules/iis_shortnames.py",
"chars": 15970,
"preview": "import re\n\nfrom bbot.modules.base import BaseModule\n\nvalid_chars = \"ETAONRISHDLFCMUGYPWBVKJXQZ0123456789_-$~()&!#%'@^`{}"
},
{
"path": "bbot/modules/internal/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "bbot/modules/internal/aggregate.py",
"chars": 419,
"preview": "from bbot.modules.report.base import BaseReportModule\n\n\nclass aggregate(BaseReportModule):\n watched_events = []\n f"
},
{
"path": "bbot/modules/internal/base.py",
"chars": 387,
"preview": "import logging\n\nfrom bbot.modules.base import BaseModule\n\n\nclass BaseInternalModule(BaseModule):\n in_scope_only = Fal"
},
{
"path": "bbot/modules/internal/cloudcheck.py",
"chars": 4951,
"preview": "import asyncio\nimport regex as re\nfrom contextlib import suppress\n\nfrom bbot.modules.base import BaseInterceptModule\n\n\nc"
},
{
"path": "bbot/modules/internal/dnsresolve.py",
"chars": 15679,
"preview": "import ipaddress\nfrom contextlib import suppress\n\nfrom bbot.errors import ValidationError\nfrom bbot.core.helpers.dns.eng"
},
{
"path": "bbot/modules/internal/excavate.py",
"chars": 64382,
"preview": "import yara\nimport json\nimport html\nimport time\nimport inspect\nimport regex as re\nfrom pathlib import Path\nfrom bbot.err"
},
{
"path": "bbot/modules/internal/speculate.py",
"chars": 9262,
"preview": "import random\nimport ipaddress\n\nfrom bbot.core.helpers import validators\nfrom bbot.modules.internal.base import BaseInte"
},
{
"path": "bbot/modules/internal/unarchive.py",
"chars": 4009,
"preview": "from pathlib import Path\nfrom contextlib import suppress\nfrom bbot.modules.internal.base import BaseInternalModule\nfrom "
},
{
"path": "bbot/modules/ip2location.py",
"chars": 2628,
"preview": "from bbot.modules.base import BaseModule\n\n\nclass IP2Location(BaseModule):\n \"\"\"\n IP2Location.io Geolocation API.\n "
},
{
"path": "bbot/modules/ipneighbor.py",
"chars": 1591,
"preview": "import ipaddress\n\nfrom bbot.modules.base import BaseModule\n\n\nclass ipneighbor(BaseModule):\n watched_events = [\"IP_ADD"
},
{
"path": "bbot/modules/ipstack.py",
"chars": 2200,
"preview": "from bbot.modules.base import BaseModule\n\n\nclass Ipstack(BaseModule):\n \"\"\"\n Ipstack GeoIP\n Leverages the ipstac"
},
{
"path": "bbot/modules/jadx.py",
"chars": 3084,
"preview": "from pathlib import Path\nfrom subprocess import CalledProcessError\nfrom bbot.modules.internal.base import BaseModule\n\n\nc"
},
{
"path": "bbot/modules/leakix.py",
"chars": 1561,
"preview": "from bbot.modules.templates.subdomain_enum import subdomain_enum_apikey\n\n\nclass leakix(subdomain_enum_apikey):\n watch"
},
{
"path": "bbot/modules/lightfuzz/lightfuzz.py",
"chars": 11581,
"preview": "import importlib\nfrom bbot.modules.base import BaseModule\n\nfrom bbot.errors import InteractshError\n\n\nclass lightfuzz(Bas"
},
{
"path": "bbot/modules/lightfuzz/submodules/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "bbot/modules/lightfuzz/submodules/base.py",
"chars": 13061,
"preview": "import copy\nimport base64\nimport binascii\nfrom urllib.parse import quote\n\n\nclass BaseLightfuzz:\n friendly_name = \"\"\n "
},
{
"path": "bbot/modules/lightfuzz/submodules/cmdi.py",
"chars": 5043,
"preview": "from bbot.errors import HttpCompareError\nfrom .base import BaseLightfuzz\n\nimport urllib.parse\n\n\nclass cmdi(BaseLightfuzz"
},
{
"path": "bbot/modules/lightfuzz/submodules/crypto.py",
"chars": 23974,
"preview": "import base64\nimport hashlib\nfrom .base import BaseLightfuzz\nfrom bbot.errors import HttpCompareError\nfrom urllib.parse "
},
{
"path": "bbot/modules/lightfuzz/submodules/esi.py",
"chars": 1558,
"preview": "from .base import BaseLightfuzz\n\n\nclass esi(BaseLightfuzz):\n \"\"\"\n Detects Edge Side Includes (ESI) processing vuln"
},
{
"path": "bbot/modules/lightfuzz/submodules/path.py",
"chars": 7744,
"preview": "from .base import BaseLightfuzz\nfrom bbot.errors import HttpCompareError\n\nfrom urllib.parse import quote\n\n\nclass path(Ba"
},
{
"path": "bbot/modules/lightfuzz/submodules/serial.py",
"chars": 9118,
"preview": "from .base import BaseLightfuzz\nfrom bbot.errors import HttpCompareError\n\n\nclass serial(BaseLightfuzz):\n \"\"\"Finds par"
},
{
"path": "bbot/modules/lightfuzz/submodules/sqli.py",
"chars": 8597,
"preview": "from .base import BaseLightfuzz\nfrom bbot.errors import HttpCompareError\n\nimport statistics\n\n\nclass sqli(BaseLightfuzz):"
},
{
"path": "bbot/modules/lightfuzz/submodules/ssti.py",
"chars": 1413,
"preview": "from .base import BaseLightfuzz\n\n\nclass ssti(BaseLightfuzz):\n \"\"\"\n Detects server-side template injection vulnerab"
},
{
"path": "bbot/modules/lightfuzz/submodules/xss.py",
"chars": 9531,
"preview": "from .base import BaseLightfuzz\n\nimport regex as re\n\n\nclass xss(BaseLightfuzz):\n \"\"\"\n Detects Reflected Cross-Site"
},
{
"path": "bbot/modules/medusa.py",
"chars": 8729,
"preview": "import re\nfrom bbot.modules.base import BaseModule\n\n\nclass medusa(BaseModule):\n watched_events = [\"PROTOCOL\"]\n pro"
},
{
"path": "bbot/modules/myssl.py",
"chars": 942,
"preview": "from bbot.modules.templates.subdomain_enum import subdomain_enum\n\n\nclass myssl(subdomain_enum):\n flags = [\"subdomain-"
},
{
"path": "bbot/modules/newsletters.py",
"chars": 2630,
"preview": "# Created a new module called 'newsletters' that will scrape the websites (or recursive websites,\n# thanks to BBOT's sub"
},
{
"path": "bbot/modules/ntlm.py",
"chars": 4391,
"preview": "from bbot.errors import NTLMError\nfrom bbot.modules.base import BaseModule\n\nntlm_discovery_endpoints = [\n \"\",\n \"au"
},
{
"path": "bbot/modules/nuclei.py",
"chars": 18005,
"preview": "import json\nimport yaml\nfrom itertools import islice\nfrom bbot.modules.base import BaseModule\n\n\nclass nuclei(BaseModule)"
},
{
"path": "bbot/modules/oauth.py",
"chars": 6720,
"preview": "from bbot.core.helpers.regexes import url_regexes\n\nfrom .base import BaseModule\n\n\nclass OAUTH(BaseModule):\n watched_e"
},
{
"path": "bbot/modules/otx.py",
"chars": 1168,
"preview": "from bbot.modules.templates.subdomain_enum import subdomain_enum_apikey\n\n\nclass otx(subdomain_enum_apikey):\n flags = "
},
{
"path": "bbot/modules/output/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "bbot/modules/output/asset_inventory.py",
"chars": 15479,
"preview": "import csv\nimport ipaddress\nfrom contextlib import suppress\n\nfrom .csv import CSV\nfrom bbot.core.helpers.misc import mak"
},
{
"path": "bbot/modules/output/base.py",
"chars": 3436,
"preview": "import logging\nfrom pathlib import Path\nfrom bbot.modules.base import BaseModule\n\n\nclass BaseOutputModule(BaseModule):\n "
},
{
"path": "bbot/modules/output/csv.py",
"chars": 2865,
"preview": "import csv\nfrom contextlib import suppress\n\nfrom bbot.modules.output.base import BaseOutputModule\n\n\nclass CSV(BaseOutput"
},
{
"path": "bbot/modules/output/discord.py",
"chars": 730,
"preview": "from bbot.modules.templates.webhook import WebhookOutputModule\n\n\nclass Discord(WebhookOutputModule):\n watched_events "
},
{
"path": "bbot/modules/output/emails.py",
"chars": 1095,
"preview": "from bbot.modules.output.txt import TXT\nfrom bbot.modules.base import BaseModule\n\n\nclass Emails(TXT):\n watched_events"
},
{
"path": "bbot/modules/output/http.py",
"chars": 2582,
"preview": "from bbot.modules.output.base import BaseOutputModule\n\n\nclass HTTP(BaseOutputModule):\n watched_events = [\"*\"]\n met"
},
{
"path": "bbot/modules/output/json.py",
"chars": 1289,
"preview": "import json\nfrom contextlib import suppress\n\nfrom bbot.modules.output.base import BaseOutputModule\n\n\nclass JSON(BaseOutp"
},
{
"path": "bbot/modules/output/mysql.py",
"chars": 1975,
"preview": "from bbot.modules.templates.sql import SQLTemplate\n\n\nclass MySQL(SQLTemplate):\n watched_events = [\"*\"]\n meta = {\n "
},
{
"path": "bbot/modules/output/neo4j.py",
"chars": 6114,
"preview": "import json\nimport logging\nfrom contextlib import suppress\nfrom neo4j import AsyncGraphDatabase\n\nfrom bbot.modules.outpu"
},
{
"path": "bbot/modules/output/nmap_xml.py",
"chars": 7048,
"preview": "import sys\nfrom xml.dom import minidom\nfrom datetime import datetime\nfrom xml.etree.ElementTree import Element, SubEleme"
},
{
"path": "bbot/modules/output/postgres.py",
"chars": 1938,
"preview": "from bbot.modules.templates.sql import SQLTemplate\n\n\nclass Postgres(SQLTemplate):\n watched_events = [\"*\"]\n meta = "
},
{
"path": "bbot/modules/output/python.py",
"chars": 270,
"preview": "from bbot.modules.output.base import BaseOutputModule\n\n\nclass python(BaseOutputModule):\n watched_events = [\"*\"]\n m"
},
{
"path": "bbot/modules/output/slack.py",
"chars": 1289,
"preview": "import yaml\n\nfrom bbot.modules.templates.webhook import WebhookOutputModule\n\n\nclass Slack(WebhookOutputModule):\n watc"
},
{
"path": "bbot/modules/output/splunk.py",
"chars": 1952,
"preview": "from bbot.errors import WebError\nfrom bbot.modules.output.base import BaseOutputModule\n\n\nclass Splunk(BaseOutputModule):"
},
{
"path": "bbot/modules/output/sqlite.py",
"chars": 979,
"preview": "from pathlib import Path\n\nfrom bbot.modules.templates.sql import SQLTemplate\n\n\nclass SQLite(SQLTemplate):\n watched_ev"
},
{
"path": "bbot/modules/output/stdout.py",
"chars": 3084,
"preview": "import json\n\nfrom bbot.logger import log_to_stderr\nfrom bbot.modules.output.base import BaseOutputModule\n\n\nclass Stdout("
},
{
"path": "bbot/modules/output/subdomains.py",
"chars": 1515,
"preview": "from bbot.modules.output.txt import TXT\nfrom bbot.modules.base import BaseModule\n\n\nclass Subdomains(TXT):\n watched_ev"
},
{
"path": "bbot/modules/output/teams.py",
"chars": 4037,
"preview": "from bbot.modules.templates.webhook import WebhookOutputModule\n\n\nclass Teams(WebhookOutputModule):\n watched_events = "
},
{
"path": "bbot/modules/output/txt.py",
"chars": 976,
"preview": "from contextlib import suppress\n\nfrom bbot.modules.output.base import BaseOutputModule\n\n\nclass TXT(BaseOutputModule):\n "
},
{
"path": "bbot/modules/output/web_parameters.py",
"chars": 2013,
"preview": "from contextlib import suppress\nfrom collections import defaultdict\n\nfrom bbot.modules.output.base import BaseOutputModu"
},
{
"path": "bbot/modules/output/web_report.py",
"chars": 3720,
"preview": "from bbot.modules.output.base import BaseOutputModule\nimport markdown\nimport html\n\n\nclass web_report(BaseOutputModule):\n"
},
{
"path": "bbot/modules/output/websocket.py",
"chars": 2721,
"preview": "import json\nimport asyncio\nimport ssl\nimport websockets\n\nfrom bbot.modules.output.base import BaseOutputModule\n\n\nclass W"
},
{
"path": "bbot/modules/paramminer_cookies.py",
"chars": 1816,
"preview": "from .paramminer_headers import paramminer_headers\n\n\nclass paramminer_cookies(paramminer_headers):\n \"\"\"\n Inspired "
},
{
"path": "bbot/modules/paramminer_getparams.py",
"chars": 1893,
"preview": "from .paramminer_headers import paramminer_headers\n\n\nclass paramminer_getparams(paramminer_headers):\n \"\"\"\n Inspire"
},
{
"path": "bbot/modules/paramminer_headers.py",
"chars": 10596,
"preview": "import re\n\nfrom bbot.errors import HttpCompareError\nfrom bbot.modules.base import BaseModule\n\n\nclass paramminer_headers("
}
]
// ... and 343 more files (download for full content)
About this extraction
This page contains the full source code of the blacklanternsecurity/bbot GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 543 files (4.7 MB), approximately 1.3M tokens, and a symbol index with 6187 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.